﻿using System;
using System.Reflection;
using System.IO;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using Microsoft.Office.InfoPath;
using System.Xml.XPath;
using System.Collections.Generic;
using System.Xml.Schema;
using InfoPathHelpers.CodeGeneration.Core;
using InfoPathHelpers.RepeatingTable;

namespace InfoPathHelpers.CodeGeneration
{
    public class PropertyGenerator : CodeDomCSharpGenerator
    {
        static FormContext _FormContext;
        static Dictionary<string, Type> _CustomTypeMapping;
        static List<NodeInfo> _NodeInfo;
        static CodeNamespace _PropertiesNamespace;
        static PropertyGenerationOptions _PropertyGenerationOptions;

        #region Common References

        private static readonly string TargetClassName = "FormCode";
        private static readonly CodeTypeReference StringTypeReference = new CodeTypeReference(typeof(String));
        private static readonly CodeTypeReference IntegerTypeReference = new CodeTypeReference(typeof(Int32));
        private static readonly CodeTypeReference BooleanTypeReference = new CodeTypeReference(typeof(Boolean));
        private static readonly CodeTypeReference GenericTypeReference = new CodeTypeReference("T");
        private static readonly CodeTypeReference GenericPredicateReference = new CodeTypeReference("Predicate", GenericTypeReference);
        private static readonly CodeTypeReference NodeInfoTypeReference = new CodeTypeReference(typeof(NodeInfo).Name);
        private static readonly CodeTypeReference GenericIEnumeratorTypeReference = new CodeTypeReference("IEnumerator", GenericTypeReference);
        private static readonly CodeTypeReference XmlFormHostItemTypeReference = new CodeTypeReference(typeof(XmlFormHostItem).Name);
        private static readonly CodeTypeReference IDataSourceContextTypeReference = new CodeTypeReference(typeof(IDataSourceContext).Name);
        private static readonly CodeTypeReference XPathNavigatorTypeReference = new CodeTypeReference(typeof(XPathNavigator).Name);
        private static readonly CodeTypeReferenceExpression NodeOperationsReference = new CodeTypeReferenceExpression(typeof(NodeOperations).Name);
        private static readonly CodeFieldReferenceExpression ContextFieldReference = new CodeFieldReferenceExpression(null, "_Context");
        private static readonly CodeFieldReferenceExpression XPathFieldReference = new CodeFieldReferenceExpression(null, "_XPath");
        private static readonly CodeFieldReferenceExpression IndexFieldReference = new CodeFieldReferenceExpression(null, "_Index");
        private static readonly CodeFieldReferenceExpression InternalListFieldReference = new CodeFieldReferenceExpression(null, "_InternalList");
        private static readonly CodeFieldReferenceExpression IsNillableFieldReference = new CodeFieldReferenceExpression(null, "_IsNillable");
        private static readonly CodeParameterDeclarationExpression FormParameterDeclaration = new CodeParameterDeclarationExpression(IDataSourceContextTypeReference, "context");
        private static readonly CodeParameterDeclarationExpression XPathParameterDeclaration = new CodeParameterDeclarationExpression(StringTypeReference, "xPath");
        private static readonly CodeParameterDeclarationExpression IndexParameterDeclaration = new CodeParameterDeclarationExpression(IntegerTypeReference, "index");
        private static readonly CodeParameterDeclarationExpression ItemParameterDeclaration = new CodeParameterDeclarationExpression(GenericTypeReference, "item");
        private static readonly CodeParameterDeclarationExpression MatchParameterDeclaration = new CodeParameterDeclarationExpression(GenericPredicateReference, "match");
        private static readonly CodeArgumentReferenceExpression FormArgumentReference = new CodeArgumentReferenceExpression(FormParameterDeclaration.Name);
        private static readonly CodeArgumentReferenceExpression XPathArgumentReference = new CodeArgumentReferenceExpression(XPathParameterDeclaration.Name);
        private static readonly CodeArgumentReferenceExpression IndexArgumentReference = new CodeArgumentReferenceExpression(IndexParameterDeclaration.Name);
        private static readonly CodeArgumentReferenceExpression ItemArgumentReference = new CodeArgumentReferenceExpression(ItemParameterDeclaration.Name);
        private static readonly CodeArgumentReferenceExpression MatchArgumentReference = new CodeArgumentReferenceExpression(MatchParameterDeclaration.Name);
        private static readonly CodePropertyReferenceExpression CountPropertyReference = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "Count");
        private static readonly CodePropertyReferenceExpression DefaultXmlPropertyReference = new CodePropertyReferenceExpression(new CodeTypeReferenceExpression("FormCode"), "DefaultXml");
        private static readonly CodePropertyReferenceExpression FormContextPropertyReference = new CodePropertyReferenceExpression(null, "Context");
        private static readonly CodeFieldReferenceExpression NewElementFieldReference = new CodeFieldReferenceExpression(null, "_NewElement");
        private static readonly CodeMethodInvokeExpression InvokeSelectXPath = new CodeMethodInvokeExpression(ContextFieldReference, "Select", XPathFieldReference);
        private static readonly CodeMethodInvokeExpression InvokeThrowIfOutOfRange = new CodeMethodInvokeExpression(null, "ThrowIfOutOfRange", IndexArgumentReference);
        
        #endregion // Common References.

        public static void Generate(FormContext formContext)
        {
            Generate(formContext, null, PropertyGenerationOptions.None);
        }

        public static void Generate(FormContext formContext, Dictionary<string, Type> customTypeMapping)
        {
            Generate(formContext, customTypeMapping, PropertyGenerationOptions.None);
        }

        public static void Generate(FormContext formContext, Dictionary<string, Type> customTypeMapping, PropertyGenerationOptions options)
        {
            _FormContext = formContext;
            _CustomTypeMapping = customTypeMapping ?? new Dictionary<string, Type>();
            _PropertyGenerationOptions = options;

            // Get the information from the schema for all nodes.
            _NodeInfo = FormInfo.GetAllNodeInfo(formContext);

            // Use the namespace of the form so it can work as a partial class.
            var FormNamespace = new CodeNamespace(_FormContext.CodeNamespace);

            // This namespace is used to hide the collection and group classes from 
            // the main FormCode class.
            _PropertiesNamespace = new CodeNamespace(FormNamespace.Name + ".Properties");

            // Add Imports to global namespace so they can be used by both 
            // FormNamespace and FormNamespace.Properties.
            var GlobalNamespace = new CodeNamespace();
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("System"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("System.Xml.XPath"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("InfoPathHelpers"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("InfoPathHelpers.RepeatingTable"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("Microsoft.Office.InfoPath"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("System.Reflection"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport("System.Text.RegularExpressions"));
            GlobalNamespace.Imports.Add(new CodeNamespaceImport(_PropertiesNamespace.Name));

            // Add the namespace to the compile unit.
            var TargetUnit = new CodeCompileUnit();
            TargetUnit.Namespaces.Add(GlobalNamespace);
            TargetUnit.Namespaces.Add(FormNamespace);
            TargetUnit.Namespaces.Add(_PropertiesNamespace);

            // Define the FormCode partial class and add it to the namespace.
            var TargetClass = new CodeTypeDeclaration(TargetClassName);
            TargetClass.IsClass = true;
            TargetClass.IsPartial = true;
            TargetClass.TypeAttributes = TypeAttributes.Public;
            FormNamespace.Types.Add(TargetClass);

            // Add a field and property used for determining whether or not the last
            // property get method was successful.
            TargetClass.Members.Add(CreateLastAccessFailedField());
            TargetClass.Members.Add(CreateLastAccessFailedProperty());
            TargetClass.Members.Add(CreateContextField());
            TargetClass.Members.Add(CreateContextProperty());

            // Create a navigator and move to the root element.
            var Root = _FormContext.Navigator;
            Root.MoveToFollowing(XPathNodeType.Element);

            // Select all nodes belonging to the root and make a field and property for each one.
            var RootNodes = _NodeInfo.FindAll(x => x.ParentName == Root.Name);
     
            if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeGroups) != PropertyGenerationOptions.ExcludeGroups)
            {
                // Add the container base class only if generating properties for groups.
                _PropertiesNamespace.Types.Add(CreateContainerBaseClass());
            }

            if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeRepeatingGroups) != PropertyGenerationOptions.ExcludeRepeatingGroups)
            {
                // Add the collection container base class and container collection base
                // classes only if generating properties for repeating groups.
                _PropertiesNamespace.Types.Add(CreateCollectionContainerBaseClass());
                _PropertiesNamespace.Types.Add(CreateGenericCollectionClass(true));
            }

            if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeRepeatingFields) != PropertyGenerationOptions.ExcludeRepeatingFields)
            {
                // Add the generic collection class only if generating properties for repeating fields.
                _PropertiesNamespace.Types.Add(CreateGenericCollectionClass(false));
            }

            // Static Dictionary used to hold DefaultXml element for repeating elements.
            var DictionaryStringStringType = new CodeTypeReference(typeof(Dictionary<string, string>).Name, StringTypeReference, StringTypeReference);
            var DefaultXmlField = new CodeMemberField(DictionaryStringStringType, "DefaultXml");
            DefaultXmlField.Attributes = MemberAttributes.Static | MemberAttributes.FamilyAndAssembly;
            var DefaultXmlFieldReference = new CodeFieldReferenceExpression(null, DefaultXmlField.Name);

            // Static constructor to build DefaultXmlDictionary;
            var StaticConstructor = new CodeTypeConstructor();
            StaticConstructor.Name = TargetClass.Name;
            var InstantiateDefaultXml = new CodeAssignStatement(DefaultXmlFieldReference, new CodeObjectCreateExpression(DictionaryStringStringType));
            StaticConstructor.Statements.Add(InstantiateDefaultXml);
            var InvokeDefaultXmlAdd = new CodeMethodReferenceExpression(DefaultXmlFieldReference, "Add");



            // ProcessNodeGroup is what will actually generate the properties.
            ProcessNodeGroup(RootNodes, TargetClass, StaticConstructor, InvokeDefaultXmlAdd, ChildTypeOptions.None);

            if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeAllRepeatingElements) != PropertyGenerationOptions.ExcludeAllRepeatingElements)
            {
                // Add the NodeMatch predicate helper class only if generating properties for repeating elements.
                _PropertiesNamespace.Types.Add(CreateNodeInfoMatchClass());

                // Add static dictionary and constructor only if generating properties for repeating elements.
                TargetClass.Members.Add(DefaultXmlField);
                TargetClass.Members.Add(StaticConstructor);
            }

            // Generate the output file.
            string OutputFileName = Directory.GetCurrentDirectory().Replace(@"bin\Debug", "FormCode.Properties.cs");
            GenerateOutputFile(OutputFileName, TargetUnit);
        }

        private static void ProcessNodeGroup(List<NodeInfo> nodes, CodeTypeDeclaration targetClass, 
            CodeTypeConstructor staticConstructor, CodeMethodReferenceExpression defaultXmlAdd, ChildTypeOptions childTypeOptions)
        {
            foreach (var Node in nodes)
            {
                // Currently not handling choice elements.
                if (Node.InfoPathType == InfoPathNodeType.ChoiceGroup)
                {
                    continue;
                }

                if (Node.IsRepeating || Node.InfoPathType == InfoPathNodeType.Group)
                {
                    var InvokeDefaultXmlAdd = new CodeMethodInvokeExpression(defaultXmlAdd,
                        new CodePrimitiveExpression(Node.XPath), new CodePrimitiveExpression(Node.DefaultElementXml));
                    staticConstructor.Statements.Add(InvokeDefaultXmlAdd);
                }

                if (Node.InfoPathType == InfoPathNodeType.Group)
                {
                    if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeGroups) == PropertyGenerationOptions.ExcludeGroups ||
                        (Node.IsRepeating && (_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeRepeatingGroups) == PropertyGenerationOptions.ExcludeRepeatingGroups))
                    {
                        // Skip groups if options are set not to include them.
                        continue;
                    }

                    // We want to handle groups seperately from elements as they require different code generation.
                    // Create the property.

                    // All groups have an XPath field.
                    var XPathField = CreateXPathField(Node, childTypeOptions);
                    targetClass.Members.Add(XPathField);

                    CodeTypeDeclaration ContainerClass;
                    CodeTypeReference ContainerType;                  
                    if (Node.IsRepeating)
                    {
                        // Repeating groups have a special kind or container.
                        ContainerClass = CreateCollectionContainerClass(Node);
                        ContainerType = new CodeTypeReference("RepeatingGroupCollection", new CodeTypeReference(ContainerClass.Name));
                    }
                    else
                    {
                        ContainerClass = CreateContainerClass(Node);
                        ContainerType = new CodeTypeReference(ContainerClass.Name);
                    }

                    // All groups have a backing field and property to get it.
                    var BackingField = CreateBackingField(Node, ContainerType);
                    var ContainerProperty = CreateComplexProperty(Node, ContainerType, childTypeOptions);
                    targetClass.Members.Add(BackingField);
                    targetClass.Members.Add(ContainerProperty);

                    // Because it is a group it may have child nodes so we process 
                    // all of them before adding the class to the namespace.
                    var GroupNodes = _NodeInfo.FindAll(x => x.ParentName == Node.Name);

                    // Make sure the IsGroup flag is set as all further nodes have a parent.
                    var ChildOptions = childTypeOptions | ChildTypeOptions.ParentIsGroup;
                    if (Node.IsRepeating)
                    {
                        // Repeating nodes should set the IsRepeating and HasRepeatingAncestor flags now.
                        ChildOptions |= ChildTypeOptions.HasRepeatingAncestor | ChildTypeOptions.ParentIsRepeating;
                    }
                    else
                    {
                        // Otherwise ensure IsRpeating flag is not set.
                        ChildOptions &= ~ChildTypeOptions.ParentIsRepeating;
                    }
                    if ((_PropertyGenerationOptions & PropertyGenerationOptions.RootOnly) != PropertyGenerationOptions.RootOnly)
                    {
                        // Continue processing nested elements only if option is not set to ignore them.
                        ProcessNodeGroup(GroupNodes, ContainerClass, staticConstructor, defaultXmlAdd, ChildOptions);
                    }
                    _PropertiesNamespace.Types.Add(ContainerClass);
                }
                else
                {
                    // Custom mapping can be used to specify the return type of 
                    // a property so we need to determine the type now.
                    CodeTypeReference NodeType;

                    if (_CustomTypeMapping.ContainsKey(Node.LocalName))
                    {
                        NodeType = new CodeTypeReference(_CustomTypeMapping[Node.LocalName]);
                    }
                    else
                    {
                        NodeType = new CodeTypeReference(FormInfo.GetCLRType(Node.XmlType).Name);
                        if (Node.IsNillable && NodeType.BaseType != "String")
                        {
                            // Nillable nodes need to have properties and fields set to Nullable<T>.
                            NodeType = new CodeTypeReference("Nullable", NodeType);
                        }
                    }

                    // Create the property.
                    if (Node.IsRepeating)
                    {
                        if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeRepeatingFields) == PropertyGenerationOptions.ExcludeRepeatingFields)
                        {
                            // Skip repeating elements if options are set not to include them.
                            continue;
                        }

                        // Repeating nodes require an indexed property.
                        var RepeatingElementCollectionType = new CodeTypeReference("RepeatingElementCollection", NodeType);
                        var XPathField = CreateXPathField(Node, childTypeOptions);
                        var BackingField = CreateBackingField(Node, RepeatingElementCollectionType);
                        var CollectionProperty = CreateComplexProperty(Node, RepeatingElementCollectionType, childTypeOptions);
                        targetClass.Members.Add(XPathField);
                        targetClass.Members.Add(BackingField);
                        targetClass.Members.Add(CollectionProperty);
                    }
                    else
                    {
                        if ((_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeFields) == PropertyGenerationOptions.ExcludeAttributes ||
                        (Node.InfoPathType == InfoPathNodeType.Attribute &&
                        (_PropertyGenerationOptions & PropertyGenerationOptions.ExcludeAttributes) == PropertyGenerationOptions.ExcludeAttributes))
                        {
                            // Skip elements or attributes if options are set not to include them.
                            continue;
                        }

                        // Normal elements just need a simple property and backing field..
                        var Field = CreateXPathField(Node, childTypeOptions);
                        if((childTypeOptions & ChildTypeOptions.HasRepeatingAncestor) == ChildTypeOptions.HasRepeatingAncestor)
                        {
                            targetClass.Members.Add(CreateBackingField(Node, NodeType));
                        }
                        var SimpleProperty = CreateSimpleProperty(Node, NodeType, childTypeOptions);
                        targetClass.Members.Add(Field);
                        targetClass.Members.Add(SimpleProperty);

                        if (Node.HasAttibutes)
                        {
                            var Attributes = _NodeInfo.FindAll(x => x.ParentName == Node.Name);
                            ProcessNodeGroup(Attributes, targetClass, staticConstructor, defaultXmlAdd, childTypeOptions | ChildTypeOptions.IsAttribute);
                        }
                    }
                }
            }
        }

        private static CodeMemberField CreateXPathField(NodeInfo node, ChildTypeOptions childTypeOptions)
        {
            var Field = new CodeMemberField();
            Field.Attributes = MemberAttributes.Private | MemberAttributes.Const;
            Field.Name = "_" + node.LocalName + "XPath";
            Field.Type = StringTypeReference;
            if ((childTypeOptions & ChildTypeOptions.ParentIsGroup) == ChildTypeOptions.ParentIsGroup)
            {
                if ((childTypeOptions & ChildTypeOptions.IsAttribute) == ChildTypeOptions.IsAttribute)
                {
                    Field.InitExpression = new CodePrimitiveExpression(string.Format("/{0}/{1}", node.ParentName,
                        node.XPath.Substring(node.XPath.LastIndexOf('/') + 1)));
                }
                else
                {
                    Field.InitExpression = new CodePrimitiveExpression(node.XPath.Substring(node.XPath.LastIndexOf('/')));
                }
            }
            else
            {
                Field.InitExpression = new CodePrimitiveExpression(node.XPath);
            }
            return Field;
        }

        private static CodeMemberField CreateBackingField(NodeInfo node, CodeTypeReference type)
        {
            var Field = new CodeMemberField();
            Field.Attributes = MemberAttributes.Private;
            Field.Name = "_" + node.LocalName;
            Field.Type = type;

            return Field;
        }

        private static CodeMemberProperty CreateSimpleProperty(NodeInfo node, CodeTypeReference type, ChildTypeOptions childTypeOptions)
        {
            // Generates the following code (* only for isAncestorRepeating):

            // public [Element.Type] [Element.Name]
            // {
            //     get
            //     {
            //*        if (_IsNew)
            //*        {
            //*            return _[Element.Name];
            //*        }
            //*        else
            //*        {
            //             [Element.Type] Value;
            //             FormCode.LastAccessFailed = !NodeOperations.GetValue<[Element.Type]>(Context, (_XPath + _[Element.Name]XPath), out Value);
            //             return Value;
            //*        }
            //     }
            //     set
            //     {
            //*        _[Element.Name] = value;
            //*        if (!_IsNew)
            //*        {
            //             NodeOperations.SetValue(Context, (_XPath + _[Element.Name]XPath), value, false);
            //*        }
            //     }
            // }

            // Reference to the field holding the XPath for the property.
            var ElementXPathFieldReference = new CodeFieldReferenceExpression(null, string.Format("_{0}XPath", node.LocalName));
            var BackingFieldReference = new CodeFieldReferenceExpression(null, string.Format("_{0}", node.LocalName));

            // Create property.
            var Property = new CodeMemberProperty();
            Property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            Property.Name = node.LocalName;
            Property.HasGet = true;
            Property.HasSet = true;
            Property.Type = type;

            CodeExpression FormContextReference;
            if((childTypeOptions & ChildTypeOptions.ParentIsGroup) == ChildTypeOptions.ParentIsGroup)
            {
                // Group containers hold their own reference in a private field.
                FormContextReference = ContextFieldReference;
            }
            else
            {
                // Properties in the root should use the Context property.
                FormContextReference = FormContextPropertyReference;
            }

            // String format used to pass the XPath to the child.
            CodeExpression XPathExpression;
            
            var IndexPropertyReference = new CodePropertyReferenceExpression(null, "Index");

            if ((childTypeOptions & ChildTypeOptions.ParentIsRepeating) == ChildTypeOptions.ParentIsRepeating)
            {
                // Fields on a repeating element need an additional index passed from the parent.
                XPathExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                    "Format", new CodePrimitiveExpression("{0}[{1}]{2}"),
                    XPathFieldReference, IndexPropertyReference, ElementXPathFieldReference);                
            }
            else
            {
                if ((childTypeOptions & ChildTypeOptions.ParentIsGroup) == ChildTypeOptions.ParentIsGroup)
                {
                    // Group members use the combined XPath.
                    XPathExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                        "Format", new CodePrimitiveExpression("{0}{1}"), XPathFieldReference, ElementXPathFieldReference);
                    
                }
                else
                {
                    // Normal fields just use the normal XPath.
                    XPathExpression = ElementXPathFieldReference;
                    
                }
                
            }

            // Value variable declaration statement.
            var ValueVariableDeclarationStatement = new CodeVariableDeclarationStatement(type, "Value");
            var ValueVariableReference = new CodeVariableReferenceExpression(ValueVariableDeclarationStatement.Name);

            // Normal getter method.
            var OutArgument = new CodeDirectionExpression(FieldDirection.Out, ValueVariableReference);
            var InvokeTryGetStatement = new CodeMethodInvokeExpression(NodeOperationsReference,
                "TryGetValue", FormContextReference, XPathExpression, OutArgument);
            var LastAccessFailedPropertyReference = new CodePropertyReferenceExpression(
                new CodeTypeReferenceExpression(TargetClassName), "LastAccessFailed");
            var AssignLastAccessedStatement  = new CodeAssignStatement(LastAccessFailedPropertyReference,
                new CodeBinaryOperatorExpression(new CodePrimitiveExpression(false),
                    CodeBinaryOperatorType.ValueEquality, InvokeTryGetStatement));

            // Value variable return statement.
            var ReturnStatement = new CodeMethodReturnStatement(ValueVariableReference);

            // Normal setter method
            var SetValueMethod = new CodeMethodInvokeExpression(NodeOperationsReference, "SetValue",
                FormContextReference, XPathExpression, new CodePropertySetValueReferenceExpression(),
                new CodePrimitiveExpression(node.IsNillable));

            if ((childTypeOptions & ChildTypeOptions.HasRepeatingAncestor) == ChildTypeOptions.HasRepeatingAncestor)
            {
                // Getter method.
                var IfIsNewStatement = CreateIfIsNewStatement(BackingFieldReference, false);
                IfIsNewStatement.FalseStatements.Add(ValueVariableDeclarationStatement);
                IfIsNewStatement.FalseStatements.Add(AssignLastAccessedStatement);
                IfIsNewStatement.FalseStatements.Add(ReturnStatement);

                // Setter method.
                var BackingFieldAssignStatement = new CodeAssignStatement(BackingFieldReference, 
                    new CodePropertySetValueReferenceExpression());
                var IfNotIsNewCondition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(null, "_IsNew"),
                    CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
                var IfNotIsNewStatement = new CodeConditionStatement(IfNotIsNewCondition);
                IfNotIsNewStatement.TrueStatements.Add(SetValueMethod);

                Property.GetStatements.Add(IfIsNewStatement);
                Property.SetStatements.Add(BackingFieldAssignStatement);
                Property.SetStatements.Add(IfNotIsNewStatement);
            }
            else
            {
                Property.GetStatements.Add(ValueVariableDeclarationStatement);
                Property.GetStatements.Add(AssignLastAccessedStatement);
                Property.GetStatements.Add(ReturnStatement);
                Property.SetStatements.Add(SetValueMethod);
            }

            return Property;
        }

        private static CodeMemberProperty CreateComplexProperty(NodeInfo node, CodeTypeReference type, ChildTypeOptions childTypeOptions)
        {
        
            // Generates the following code (* only for isAncestorRepeating):

            // public [Element.Name]Container [Element.Name]
            // {
            //     get
            //     {
            //         if ((_[Element.Name] == null))
            //         {
            //*             if (_IsNew)
            //*             {
            //*                 _[Element.Name] = new [Element.Name]Container();
            //*             }
            //*             else
            //*             {
            //                 _[Element.Name] = new [Element.Name]Container(_Context, string.Format("{0}[{1}]", _[Element.Name]XPath, Index));
            //*             }
            //         }
            //         return _[Element.Name];
            //     }
            // }

            // Reference to the field holding the XPath for the property.
            var ElementXPathFieldReference = new CodeFieldReferenceExpression(null, string.Format("_{0}XPath", node.LocalName));
            var BackingFieldReference = new CodeFieldReferenceExpression(null, string.Format("_{0}", node.LocalName));

            CodeExpression FormContextReference;
            if ((childTypeOptions & ChildTypeOptions.ParentIsGroup) == ChildTypeOptions.ParentIsGroup)
            {
                // Group containers hold their own reference in a private field.
                FormContextReference = ContextFieldReference;
            }
            else
            {
                // Properties in the root should use the Context property.
                FormContextReference = FormContextPropertyReference;
            }

            // Create property.
            var Property = new CodeMemberProperty();
            Property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            Property.Name = node.LocalName;
            Property.HasGet = true;
            Property.Type = type;

            var CombinedXPath = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                    "Format", new CodePrimitiveExpression("{0}{1}"), XPathFieldReference, ElementXPathFieldReference);

            // String format used to pass the XPath to the child.
            CodeExpression XPathExpression;
            var IndexPropertyReference = new CodePropertyReferenceExpression(null, "Index");
            CodeIndexerExpression DefaultXmlIndexReference;

            if ((childTypeOptions & ChildTypeOptions.ParentIsRepeating) == ChildTypeOptions.ParentIsRepeating)
            {
                // Fields on a repeating element need an additional index passed from the parent.
                XPathExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                    "Format", new CodePrimitiveExpression("{0}[{1}]{2}"),
                    XPathFieldReference, IndexPropertyReference, ElementXPathFieldReference);
                DefaultXmlIndexReference = new CodeIndexerExpression(DefaultXmlPropertyReference, CombinedXPath);
            }
            else
            {
                if ((childTypeOptions & ChildTypeOptions.ParentIsGroup) == ChildTypeOptions.ParentIsGroup)
                {
                    // Group members use the combined XPath.
                    XPathExpression = CombinedXPath;
                    DefaultXmlIndexReference = new CodeIndexerExpression(DefaultXmlPropertyReference, CombinedXPath);
                }
                else
                {
                    // Normal fields just use the normal XPath.
                    XPathExpression = ElementXPathFieldReference;
                    DefaultXmlIndexReference = new CodeIndexerExpression(DefaultXmlPropertyReference, ElementXPathFieldReference);
                }

            }

            // Test for null.
            var IfStatementCondition = new CodeBinaryOperatorExpression(
                BackingFieldReference, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));

            

            
            CodeObjectCreateExpression CreateObject;
            if (node.IsRepeating)
            {
                if (node.InfoPathType == InfoPathNodeType.Group)
                {
                    // Repeating groups need an additional newElement parameter passed to the constructor.
                    CreateObject = new CodeObjectCreateExpression(type, FormContextReference, XPathExpression,
                        DefaultXmlIndexReference);
                }
                else
                {
                    // Repeating elements need additional newElement and isNillable parameters passed to the constructor.
                    CreateObject = new CodeObjectCreateExpression(type, FormContextReference, XPathExpression,
                        DefaultXmlIndexReference, new CodePrimitiveExpression(node.IsNillable));
                }
            }
            else
            {
                // Otherwise just the _Form and _XPath.
                CreateObject = new CodeObjectCreateExpression(type, FormContextReference, XPathExpression);
            }
            var AssignObjectToBackingField = new CodeAssignStatement(BackingFieldReference, CreateObject);

            CodeStatement IfTrueStatement;
            if ((childTypeOptions & ChildTypeOptions.HasRepeatingAncestor) == ChildTypeOptions.HasRepeatingAncestor)
            {
                // If there is a repeating ancestor it may be necessary to assign a default object to
                // the backing field.
                var CreateDefaultObject = new CodeObjectCreateExpression(type);
                var AssignDefaultToBackingField = new CodeAssignStatement(BackingFieldReference, CreateDefaultObject);
                var IsNewCondtion = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(null, "_IsNew"),
                    CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
                var IfIsNewStatement = new CodeConditionStatement(IsNewCondtion);
                IfIsNewStatement.TrueStatements.Add(AssignDefaultToBackingField);
                IfIsNewStatement.FalseStatements.Add(AssignObjectToBackingField);
                
                IfTrueStatement = IfIsNewStatement;
            }
            else
            {
                // Otherwise we just assign a new object.
                IfTrueStatement = AssignObjectToBackingField;
            }

            var IfStatement = new CodeConditionStatement(IfStatementCondition, IfTrueStatement);
            var ReturnStatement = new CodeMethodReturnStatement(BackingFieldReference);

            Property.GetStatements.Add(IfStatement);
            Property.GetStatements.Add(ReturnStatement);

            return Property;
        }

        private static CodeTypeDeclaration CreateContainerClass(NodeInfo node)
        {
            // public class [Group.Name]Container : ContainerBase
            // {
            //     private const string _[Element.Name]XPath = "[Element.XPath]";
            //
            //     public [Group.Name]Container() : base (null, null)
            //     {
            //         _IsNew = true;
            //     }
            //
            //     public [Group.Name]Container(IDataSourceContext context, string xPath)
            //         :
            //             base(context, xPath)
            //     {
            //         _IsNew = false;
            //     }
            //
            //     public [Element.Type] [Element.Name]
            //     {
            //         get
            //         {
            //             if (_IsNew)
            //             {
            //                 return _[Element.Name];
            //             }
            //             else
            //             {
            //                 return NodeOperations.GetValue<[Element.Type]>(_Context, (_XPath + _[Element.Name]XPath));
            //             }
            //         }
            //         set
            //         {
            //             _[ElementName] = value;
            //             if (!_IsNew)
            //             {
            //                 NodeOperations.SetValue(_Context, (_XPath + _[Element.Name]XPath), value, [Element.IsNillable]);
            //             }
            //         }
            //     }
            //     private string _[Element.Name];
            // }

            // Create the class.
            var ContainerClass = new CodeTypeDeclaration(node.LocalName + "Container");
            ContainerClass.IsClass = true;
            ContainerClass.BaseTypes.Add(new CodeTypeReference("ContainerBase"));

            // Create a default constructor.
            var DefaultConstructor = new CodeConstructor();
            DefaultConstructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(null));
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(null));


            // Create a constructor to set the fields in the base class.
            var Constructor = new CodeConstructor();
            Constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            Constructor.Parameters.Add(FormParameterDeclaration);
            Constructor.Parameters.Add(XPathParameterDeclaration);
            Constructor.BaseConstructorArgs.Add(FormArgumentReference);
            Constructor.BaseConstructorArgs.Add(XPathArgumentReference);

            ContainerClass.Members.Add(DefaultConstructor);
            ContainerClass.Members.Add(Constructor);

            return ContainerClass;
        }

        private static CodeTypeDeclaration CreateCollectionContainerClass(NodeInfo node)
        {
            // Generates the following code:

            // public override void SetValues(IDataSourceContext context, string xPath, int index)
            // {
            //     _Context = context;
            //     _XPath = xPath;
            //     _Index = index;
            //
            //     Type Type = this.GetType();
            //     PropertyInfo[] Properties = Type.GetProperties();
            //     for (int i = 0; i < Properties.Length; i++)
            //     {
            //         PropertyInfo Property = Properties[i];
            //         FieldInfo Field = Type.GetField("_" + Property.Name, BindingFlags.Instance | BindingFlags.NonPublic);
            //         if (Property.CanWrite)
            //         {
            //             Properties[i].SetValue(this, Field.GetValue(this), null);
            //         }
            //     }
            // }

            // Create the class.
            var ContainerClass = new CodeTypeDeclaration(node.LocalName + "Container");
            ContainerClass.IsClass = true;
            ContainerClass.BaseTypes.Add(new CodeTypeReference("RepeatingContainerBase"));

            // Create a default constructor.
            var DefaultConstructor = new CodeConstructor();
            DefaultConstructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(null));
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(null));
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(0));

            // Create a constructor to set the fields in the base class.
            var Constructor = new CodeConstructor();
            Constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            Constructor.Parameters.Add(FormParameterDeclaration);
            Constructor.Parameters.Add(XPathParameterDeclaration);
            Constructor.Parameters.Add(IndexParameterDeclaration);
            Constructor.BaseConstructorArgs.Add(FormArgumentReference);
            Constructor.BaseConstructorArgs.Add(XPathArgumentReference);
            Constructor.BaseConstructorArgs.Add(IndexArgumentReference);

            

            // Add statements.
            ContainerClass.Members.Add(DefaultConstructor);
            ContainerClass.Members.Add(Constructor);

            return ContainerClass;
        }

        #region Collections

        private static CodeTypeDeclaration CreateGenericCollectionClass(bool isGroup)
        {
            // Create the class.
            var CollectionClass = new CodeTypeDeclaration();
            if (isGroup)
            {
                CollectionClass.Name = "RepeatingGroupCollection";
            }
            else
            {
                CollectionClass.Name = "RepeatingElementCollection";
            }
            CollectionClass.IsClass = true;
            CollectionClass.BaseTypes.Add(new CodeTypeReference("ContainerBase"));
            CollectionClass.BaseTypes.Add(new CodeTypeReference("IList", GenericTypeReference));
            CollectionClass.TypeParameters.Add(new CodeTypeParameter("T"));

            // Create a field to hold string for default element.
            var NewElementField = new CodeMemberField(StringTypeReference, "_NewElement");
            NewElementField.Attributes = MemberAttributes.Private;
            var NewElementFieldReference = new CodeFieldReferenceExpression(null, NewElementField.Name);

            // Create a field to hold the internal list.
            var InternalListField = new CodeMemberField(new CodeTypeReference("List", GenericTypeReference), "_InternalList");
            NewElementField.Attributes = MemberAttributes.Private;
            
            // Create a default constructor.
            var DefaultConstructor = new CodeConstructor();
            DefaultConstructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(null));
            DefaultConstructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(null));

            var IsNewAssignTrueStatement = new CodeAssignStatement(new CodeFieldReferenceExpression(null, "_IsNew"), 
                new CodePrimitiveExpression(true));
            var InternalListAssignStatement = new CodeAssignStatement(InternalListFieldReference, new CodeObjectCreateExpression(
                new CodeTypeReference("List", GenericTypeReference)));
            DefaultConstructor.Statements.Add(IsNewAssignTrueStatement);
            DefaultConstructor.Statements.Add(InternalListAssignStatement);

            // Create a constructor to set the fields in the base class.
            var Constructor = new CodeConstructor();
            Constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            Constructor.Parameters.Add(FormParameterDeclaration);
            Constructor.Parameters.Add(XPathParameterDeclaration);
            Constructor.Parameters.Add(new CodeParameterDeclarationExpression(StringTypeReference, "newElement"));
            Constructor.BaseConstructorArgs.Add(FormArgumentReference);
            Constructor.BaseConstructorArgs.Add(XPathArgumentReference);

            var IsNewAssignFalseStatement = new CodeAssignStatement(new CodeFieldReferenceExpression(null, "_IsNew"),
                new CodePrimitiveExpression(false));
            Constructor.Statements.Add(IsNewAssignFalseStatement);
            Constructor.Statements.Add(new CodeAssignStatement(NewElementFieldReference,
                new CodeArgumentReferenceExpression("newElement")));
            if (!isGroup)
            {
                Constructor.Parameters.Add(new CodeParameterDeclarationExpression(BooleanTypeReference, "isNillable"));
                Constructor.Statements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(null, "_IsNillable"),
                new CodeArgumentReferenceExpression("isNillable")));
            }
            
            // Add all members to repsective classes.
            CollectionClass.Members.Add(NewElementField);
            CollectionClass.Members.Add(InternalListField);
            CollectionClass.Members.Add(DefaultConstructor);
            CollectionClass.Members.Add(Constructor);
            CollectionClass.Members.Add(CreateInsertFirstElementMethod());
            CollectionClass.Members.Add(CreateInsertMethod());
            CollectionClass.Members.Add(CreateRemoveAtMethod());
            CollectionClass.Members.Add(CreateTryRemoveAtMethod());
            
            CollectionClass.Members.Add(CreateAddMethod());
            CollectionClass.Members.Add(CreateInternalAddMethod());
            CollectionClass.Members.Add(CreateClearMethod());
            CollectionClass.Members.Add(CreateClearOverloadMethod());
            CollectionClass.Members.Add(CreateCopyToMethod());
            CollectionClass.Members.Add(CreateRemoveMethod());
            CollectionClass.Members.Add(CreateCountProperty());
            CollectionClass.Members.Add(CreateIsReadOnlyProperty());
            CollectionClass.Members.Add(CreateGetEnumeratorMethod());
            CollectionClass.Members.Add(CreateGetEnumeratorTMethod());
            if (isGroup)
            {
                CollectionClass.Members.Add(CreateContainerAddEmptyMethod());
                CollectionClass.Members.Add(CreateContainerCollectionIndexer());
                CollectionClass.Members.Add(CreateContainerIndexOfMethod());
                CollectionClass.Members.Add(CreateContainerContainsMethod());
                CollectionClass.Members.Add(CreateFindAllPredicateMethod());
                CollectionClass.Members.Add(CreateFindAllStringMethod());
                CollectionClass.Members.Add(CreateFindNavigatorMethod());
                CollectionClass.Members.Add(CreateFindPredicateMethod());
                CollectionClass.Members.Add(CreateFindStringMethod());
            }
            else
            {
                CollectionClass.Members.Add(CreateAddEmptyMethod());
                CollectionClass.Members.Add(CreateIsNillableField());
                CollectionClass.Members.Add(CreateCollectionIndexer());
                CollectionClass.Members.Add(CreateIndexOfMethod());
                CollectionClass.Members.Add(CreateContainsMethod());         
            }
            CollectionClass.Members.Add(CreateCollectionSetValuesMethod());
            CollectionClass.Members.Add(CreateThrowIfOutOfRangeMethod());

            return CollectionClass;
        }

        private static CodeMethodInvokeExpression CreateInvokeNavigatorExpression(CodeExpression xPathExpression)
        {
            var InvokeGetNavigator = new CodeMethodInvokeExpression(NodeOperationsReference,
                "GetNavigator", ContextFieldReference, xPathExpression);

            return InvokeGetNavigator;
        }

        private static CodeMethodInvokeExpression CreateInvokeStringFormatExpression(CodeExpression indexExpression)
        {
            var InvokeStringFormat = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference), "Format",
                new CodePrimitiveExpression("{0}[{1}]"), XPathFieldReference, indexExpression);

            return InvokeStringFormat;
        }

        private static CodeConditionStatement CreateIfIsNewStatement(CodeExpression expression, bool isVoidReturn)
        {
            // Generates the follwoing code (* for non void returns):

            // if (_IsNew == true)
            // {
            //*    return [invokeExpression];
            //     [invokeExpression];
            //     return;
            // }

            var IsNewCondition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(null, "_IsNew"),
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
            var IfIsNewStatement = new CodeConditionStatement(IsNewCondition);
            if (isVoidReturn)
            {
                IfIsNewStatement.TrueStatements.Add(expression);
                IfIsNewStatement.TrueStatements.Add(new CodeMethodReturnStatement());
            }
            else
            {
                IfIsNewStatement.TrueStatements.Add(new CodeMethodReturnStatement(expression));
            }

            return IfIsNewStatement;
        }

        private static CodeConditionStatement CreateIfIsNewStatement(CodeStatement statement)
        {
            // Generates the follwoing code (* for non void returns):

            // if (_IsNew == true)
            // {
            //     [statement];
            //     return;
            // }

            var IsNewCondition = new CodeBinaryOperatorExpression(new CodeFieldReferenceExpression(null, "_IsNew"),
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
            var IfIsNewStatement = new CodeConditionStatement(IsNewCondition);
            IfIsNewStatement.TrueStatements.Add(statement);
            IfIsNewStatement.TrueStatements.Add(new CodeMethodReturnStatement());

            return IfIsNewStatement;
        }

        #region Helper Methods

        private static CodeTypeDeclaration CreateNodeInfoMatchClass()
        {
            // Generates the following code:

            // internal class NodeInfoMatch
            // {
            //     private string _SearchTerm;
            //
            //     public NodeInfoMatch(string searchTerm)
            //     {
            //         _SearchTerm = searchTerm;
            //     }
            //
            //     private bool NodeInfoMatchName(NodeInfo node)
            //     {
            //         return (node.Name == _SearchTerm);
            //     }
            //
            //     public Predicate<NodeInfo> MatchName
            //     {
            //         get { return NodeInfoMatchName; }
            //     }
            //
            //     private bool NodeInfoMatchXPath(NodeInfo node)
            //     {
            //         return (node.XPath == _SearchTerm);
            //     }
            //
            //     public Predicate<NodeInfo> MatchXPath
            //     {
            //         get { return NodeInfoMatchXPath; }
            //     }
            // }

            var NodeInfoMatchClass = new CodeTypeDeclaration("NodeInfoMatch");
            NodeInfoMatchClass.Attributes = MemberAttributes.FamilyAndAssembly;

            // SearchTerm Field.
            var SearchTermField = new CodeMemberField(StringTypeReference, "_SearchTerm");
            var SearchTermFieldReference = new CodeFieldReferenceExpression(null, SearchTermField.Name);

            // Constructor.
            var Constructor = new CodeConstructor();
            Constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            var SearchTermParameter = new CodeParameterDeclarationExpression(StringTypeReference, "searchTerm");
            var SearchTermArgumentReference = new CodeArgumentReferenceExpression(SearchTermParameter.Name);
            var SearchTermAssignStatement = new CodeAssignStatement(SearchTermFieldReference, SearchTermArgumentReference);
            Constructor.Parameters.Add(SearchTermParameter);
            Constructor.Statements.Add(SearchTermAssignStatement);

            // NodeInfoMatchName method.
            var MatchNameMethod = new CodeMemberMethod();
            MatchNameMethod.Name = "NodeInfoMatchName";
            MatchNameMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
            MatchNameMethod.ReturnType = BooleanTypeReference;
            var NodeParameter = new CodeParameterDeclarationExpression(NodeInfoTypeReference, "node");
            var NodeArgumentReference = new CodeArgumentReferenceExpression(NodeParameter.Name);
            MatchNameMethod.Parameters.Add(NodeParameter);
            var NodeNamePropertyReference = new CodePropertyReferenceExpression(NodeArgumentReference, "Name");
            var NodeNameEqualsSearchTerm = new CodeBinaryOperatorExpression(NodeNamePropertyReference, 
                CodeBinaryOperatorType.ValueEquality, SearchTermFieldReference);
            var MatchNameReturnStatment = new CodeMethodReturnStatement(NodeNameEqualsSearchTerm);
            MatchNameMethod.Statements.Add(MatchNameReturnStatment);
            var MatchNameMethodReference = new CodeMethodReferenceExpression(null, MatchNameMethod.Name);

            // NodeInfoMatchXPath method.
            var MatchXPathMethod = new CodeMemberMethod();
            MatchXPathMethod.Name = "NodeInfoMatchXPath";
            MatchXPathMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
            MatchXPathMethod.ReturnType = BooleanTypeReference;
            MatchXPathMethod.Parameters.Add(NodeParameter);
            var NodeXPathPropertyReference = new CodePropertyReferenceExpression(NodeArgumentReference, "XPath");
            var NodeXPathEqualsSearchTerm = new CodeBinaryOperatorExpression(NodeXPathPropertyReference,
                CodeBinaryOperatorType.ValueEquality, SearchTermFieldReference);
            var MatchXPathReturnStatment = new CodeMethodReturnStatement(NodeXPathEqualsSearchTerm);
            MatchXPathMethod.Statements.Add(MatchXPathReturnStatment);
            var MatchXPathMethodReference = new CodeMethodReferenceExpression(null, MatchXPathMethod.Name);

            // MatchName property.
            var MatchNameProperty = new CodeMemberProperty();
            MatchNameProperty.Name = "MatchName";
            MatchNameProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            MatchNameProperty.Type = new CodeTypeReference(typeof(Predicate<NodeInfo>).Name, NodeInfoTypeReference);
            MatchNameProperty.HasGet = true;
            MatchNameProperty.HasSet = false;
            MatchNameProperty.GetStatements.Add(new CodeMethodReturnStatement(MatchNameMethodReference));

            // MatchXPath property.
            var MatchXPathProperty = new CodeMemberProperty();
            MatchXPathProperty.Name = "MatchXPath";
            MatchXPathProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            MatchXPathProperty.Type = new CodeTypeReference(typeof(Predicate<NodeInfo>).Name, NodeInfoTypeReference);
            MatchXPathProperty.HasGet = true;
            MatchXPathProperty.HasSet = false;
            MatchXPathProperty.GetStatements.Add(new CodeMethodReturnStatement(MatchXPathMethodReference));

            // Add members.
            NodeInfoMatchClass.Members.Add(SearchTermField);
            NodeInfoMatchClass.Members.Add(Constructor);
            NodeInfoMatchClass.Members.Add(MatchNameMethod);
            NodeInfoMatchClass.Members.Add(MatchXPathMethod);
            NodeInfoMatchClass.Members.Add(MatchNameProperty);
            NodeInfoMatchClass.Members.Add(MatchXPathProperty);

            return NodeInfoMatchClass;
        }

        private static CodeMemberMethod CreateInsertFirstElementMethod()
        {
            // Generates the following code:

            // private void InsertFirstElement(string xPath, string newElement)
            // {
            //     string ParentXPath = xPath.Remove(xPath.LastIndexOf('/'));
            //     string ParentName = RemoveIndexesFromXPath(ParentXPath
            //         .Substring((ParentXPath.LastIndexOf(':') + 1)));
            //     List<NodeInfo> Nodes = FormInfo.GetAllNodeInfo(_FormContext, ParentName);
            //     NodeInfo CurrentNode = Nodes.Find(new NodeInfoMatch(RemoveIndexesFromXPath(xPath)).MatchXPath);
            //     if ((string.IsNullOrEmpty(CurrentNode.PreviousSiblingName) == false))
            //     {
            //         for (;;)
            //         {
            //             CurrentNode = Nodes.Find(new NodeInfoMatch(CurrentNode.PreviousSiblingName).MatchName);
            //             string XPath = ParentXPath + "/" + CurrentNode.Name;
            //             if ((CurrentNode.IsRepeating == true))
            //             {
            //                 XPath = XPath + "[last()]";
            //             }
            //             XPathNavigator SiblingNode = NodeOperations.GetNavigator(_Form, XPath);
            //             if (SiblingNode != null)
            //             {
            //                 SiblingNode.InsertAfter(newElement);
            //                 return;
            //             }
            //         }
            //     }
            //     else
            //     {
            //         XPathNavigator ParentNode = NodeOperations.GetNavigator(_Form, ParentXPath);
            //         if (ParentNode != null)
            //         {
            //             ParentNode.PrependChild(newElement);
            //         }
            //         else
            //         {
            //             InsertFirstElement(ParentXPath, FormCode.DefaultXml[ParentXPath]);
            //         }
            //     }
            // }

            // Create method.
            var InsertFirstElementMethod = new CodeMemberMethod();
            InsertFirstElementMethod.Attributes = MemberAttributes.Private | MemberAttributes.Final;
            InsertFirstElementMethod.Name = "InsertFirstElement";
            InsertFirstElementMethod.Parameters.Add(XPathParameterDeclaration);
            InsertFirstElementMethod.Parameters.Add(new CodeParameterDeclarationExpression(StringTypeReference, "newElement"));

            var NewElementArgumentReference = new CodeArgumentReferenceExpression("newElement");

            // ParentXPath variable declaration statement.
            var InvokeLastIndexOfXPath = new CodeMethodInvokeExpression(XPathArgumentReference, "LastIndexOf",
                new CodePrimitiveExpression('/'));
            var InvokeRemoveXPath = new CodeMethodInvokeExpression(XPathArgumentReference, "Remove", InvokeLastIndexOfXPath);
            var ParentXPathVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "ParentXPath",
                InvokeRemoveXPath);
            var ParentXPathVariableReference = new CodeVariableReferenceExpression(ParentXPathVariableDeclarationStatement.Name);

            // ParentName variable declaration statement.
            var InvokeLastIndexOfParentXPath = new CodeMethodInvokeExpression(ParentXPathVariableReference, "LastIndexOf",
                new CodePrimitiveExpression(':'));
            var InvokeRemoveParentXPath = new CodeMethodInvokeExpression(ParentXPathVariableReference, "Substring",
                new CodeBinaryOperatorExpression(InvokeLastIndexOfParentXPath, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));
            var InvokeRemoveIndexes = new CodeMethodInvokeExpression(null, "RemoveIndexesFromXPath",
                InvokeRemoveParentXPath);
            var ParentNameVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "ParentName",
                InvokeRemoveIndexes);
            var ParentNameVariableReference = new CodeVariableReferenceExpression(ParentNameVariableDeclarationStatement.Name);

            // Nodes variable declaration statement.
            var InvokeGetAllNodeInfo = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("FormInfo"),
                "GetAllNodeInfo", new CodeCastExpression(new CodeTypeReference(typeof(FormContext).Name),
                    ContextFieldReference), ParentNameVariableReference);
            var ListNodeInfoTypeReference = new CodeTypeReference(typeof(List<NodeInfo>).Name, NodeInfoTypeReference);
            var NodesVariableDeclarationStatement = new CodeVariableDeclarationStatement(ListNodeInfoTypeReference, "Nodes",
                InvokeGetAllNodeInfo);
            var NodesVariableReference = new CodeVariableReferenceExpression(NodesVariableDeclarationStatement.Name);

            // CurrentNode variable declaration statement.
            var InvokeRemoveIndexesFromXPath = new CodeMethodInvokeExpression(null, "RemoveIndexesFromXPath", XPathArgumentReference);
            var NewNodeInfoMatchXPath = new CodeObjectCreateExpression(new CodeTypeReference("NodeInfoMatch"), InvokeRemoveIndexesFromXPath);
            var NodeInfoMatchXPathProperty = new CodePropertyReferenceExpression(NewNodeInfoMatchXPath, "MatchXPath");
            var InvokeFindXPath = new CodeMethodInvokeExpression(NodesVariableReference, "Find", NodeInfoMatchXPathProperty);
            var CurrentNodeVariableDeclarationStatment = new CodeVariableDeclarationStatement(NodeInfoTypeReference, "CurrentNode",
                InvokeFindXPath);
            var CurrentNodeVariableReference = new CodeVariableReferenceExpression(CurrentNodeVariableDeclarationStatment.Name);

            // If true statements.
            var PreviousSiblingNamePropertyReference = new CodePropertyReferenceExpression(CurrentNodeVariableReference,
                "PreviousSiblingName");
            var NewNodeInfoMatchName = new CodeObjectCreateExpression(new CodeTypeReference("NodeInfoMatch"), PreviousSiblingNamePropertyReference);
            var NodeInfoMatchNameProperty = new CodePropertyReferenceExpression(NewNodeInfoMatchName, "MatchName");
            var InvokeFindName = new CodeMethodInvokeExpression(NodesVariableReference, "Find", NodeInfoMatchNameProperty);
            var CurrentNodeAssignmentStatement = new CodeAssignStatement(CurrentNodeVariableReference, InvokeFindName);

            var CurrentNodeNameProperty = new CodePropertyReferenceExpression(CurrentNodeVariableReference, "Name");
            var XPathVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "XPath",
                new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference), "Format",
                    new CodePrimitiveExpression("{0}/{1}"), ParentXPathVariableReference, CurrentNodeNameProperty));
            var XPathVariableReference = new CodeVariableReferenceExpression(XPathVariableDeclarationStatement.Name);

            var CurrentNodeIsRepeatingProperty = new CodePropertyReferenceExpression(CurrentNodeVariableReference, "IsRepeating");
            var CurrentNodeIsRepeatingCondition = new CodeBinaryOperatorExpression(CurrentNodeIsRepeatingProperty,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
            var XPathConcatExpression = new CodeBinaryOperatorExpression(XPathVariableReference,
                CodeBinaryOperatorType.Add, new CodePrimitiveExpression("[last()]"));
            var XPathAssignStatement = new CodeAssignStatement(XPathVariableReference, XPathConcatExpression);
            var IfIsRepeatingStatement = new CodeConditionStatement(CurrentNodeIsRepeatingCondition);
            IfIsRepeatingStatement.TrueStatements.Add(XPathAssignStatement);

            var SiblingNodeVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference,
                "SiblingNode", CreateInvokeNavigatorExpression(XPathVariableReference));
            var SiblingNodeVariableReference = new CodeVariableReferenceExpression(SiblingNodeVariableDeclarationStatement.Name);

            var SiblingNodeNotNullCondition = new CodeBinaryOperatorExpression(SiblingNodeVariableReference,
                CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));

            var InvokeSiblingNodeInsertAfterStatement = new CodeMethodInvokeExpression(SiblingNodeVariableReference, "InsertAfter",
                NewElementArgumentReference);


            var IfSiblingNodeNotNullStatement = new CodeConditionStatement(SiblingNodeNotNullCondition);
            IfSiblingNodeNotNullStatement.TrueStatements.Add(InvokeSiblingNodeInsertAfterStatement);
            IfSiblingNodeNotNullStatement.TrueStatements.Add(new CodeMethodReturnStatement());

            var WhileLoopStatement = new CodeIterationStatement();
            WhileLoopStatement.InitStatement = new CodeSnippetStatement();
            WhileLoopStatement.TestExpression = new CodeSnippetExpression();
            WhileLoopStatement.IncrementStatement = new CodeSnippetStatement();

            WhileLoopStatement.Statements.Add(CurrentNodeAssignmentStatement);
            WhileLoopStatement.Statements.Add(XPathVariableDeclarationStatement);
            WhileLoopStatement.Statements.Add(IfIsRepeatingStatement);
            WhileLoopStatement.Statements.Add(SiblingNodeVariableDeclarationStatement);
            WhileLoopStatement.Statements.Add(IfSiblingNodeNotNullStatement);

            // If false statements.
            var ParentNodeVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference,
                "ParentNode", CreateInvokeNavigatorExpression(ParentXPathVariableReference));
            var ParentNodeVariableReference = new CodeVariableReferenceExpression(ParentNodeVariableDeclarationStatement.Name);

            var ParentNodeNotNullCondition = new CodeBinaryOperatorExpression(ParentNodeVariableReference,
                CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));

            var InvokeParentNodePrependChildStatement = new CodeMethodInvokeExpression(ParentNodeVariableReference, "PrependChild",
                NewElementArgumentReference);

            var InvokeInsertFirstElement = new CodeMethodInvokeExpression(null, "InsertFirstElement", ParentXPathVariableReference,
                new CodeIndexerExpression(DefaultXmlPropertyReference, ParentXPathVariableReference));

            var IfParentNodeNotNullStatement = new CodeConditionStatement(ParentNodeNotNullCondition);
            IfParentNodeNotNullStatement.TrueStatements.Add(InvokeParentNodePrependChildStatement);
            IfParentNodeNotNullStatement.FalseStatements.Add(InvokeInsertFirstElement);

            // If statement.
            var InvokeStringIsNullOrEmpty = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                "IsNullOrEmpty", PreviousSiblingNamePropertyReference);
            var PreviousSiblingIsNotNullCondition = new CodeBinaryOperatorExpression(InvokeStringIsNullOrEmpty,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
            var IfPreviousSiblingIsNotNullStatement = new CodeConditionStatement(PreviousSiblingIsNotNullCondition);

            IfPreviousSiblingIsNotNullStatement.TrueStatements.Add(WhileLoopStatement);

            IfPreviousSiblingIsNotNullStatement.FalseStatements.Add(ParentNodeVariableDeclarationStatement);
            IfPreviousSiblingIsNotNullStatement.FalseStatements.Add(IfParentNodeNotNullStatement);

            // Add all statements.
            InsertFirstElementMethod.Statements.Add(ParentXPathVariableDeclarationStatement);
            InsertFirstElementMethod.Statements.Add(ParentNameVariableDeclarationStatement);
            InsertFirstElementMethod.Statements.Add(NodesVariableDeclarationStatement);
            InsertFirstElementMethod.Statements.Add(CurrentNodeVariableDeclarationStatment);
            InsertFirstElementMethod.Statements.Add(IfPreviousSiblingIsNotNullStatement);


            return InsertFirstElementMethod;
        }

        private static CodeMemberMethod CreateCollectionSetValuesMethod()
        {
            // Generates the following code:

            // public override void SetValues(XmlFormHostItem form, string xPath)
            // {
            //     _Form = form;
            //     _XPath = xPath;
            //     _IsNew = false;
            //     _NewElement = FormCode.DefaultXml[RemoveIndexesFromXPath(_XPath)];
            //     this.Clear();
            //     for (int i = 0; (i < _InternalList.Count); i = (i + 1))
            //     {
            //         this.Add(_InternalList[i]);
            //     }
            // }

            // Create method.
            var SetValuesMethod = new CodeMemberMethod();
            SetValuesMethod.Name = "SetValues";
            SetValuesMethod.Attributes = MemberAttributes.Public | MemberAttributes.Override;
            SetValuesMethod.Parameters.Add(FormParameterDeclaration);
            SetValuesMethod.Parameters.Add(XPathParameterDeclaration);
            
            // DefaultXml Indexer
            var InvokeRemoveIndexes = new CodeMethodInvokeExpression(null, "RemoveIndexesFromXPath", XPathFieldReference);
            var DefaultXmlIndexer = new CodeIndexerExpression(DefaultXmlPropertyReference, InvokeRemoveIndexes);

            // Field assignment statements.
            var FormAssignStatement = new CodeAssignStatement(ContextFieldReference, FormArgumentReference);
            var XPathAssignStatement = new CodeAssignStatement(XPathFieldReference, XPathArgumentReference);
            var IsNewAssignStatement = new CodeAssignStatement(new CodeFieldReferenceExpression(null, "_IsNew"),
                new CodePrimitiveExpression(false));
            var NewElementAssignStatement = new CodeAssignStatement(NewElementFieldReference, DefaultXmlIndexer);

            // Invoke this.Clear()
            var InvokeClearStatement = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Clear");

            // For loop statement.
            var ForLoopInitStatement = new CodeVariableDeclarationStatement(IntegerTypeReference, "i", new CodePrimitiveExpression(0));
            var InternalListCountPropertyReference = new CodePropertyReferenceExpression(InternalListFieldReference, "Count");
            var ForLoopTestExpression = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression(ForLoopInitStatement.Name),
                CodeBinaryOperatorType.LessThan, InternalListCountPropertyReference);
            var ForLoopIncrementStatement = new CodeAssignStatement(new CodeVariableReferenceExpression(ForLoopInitStatement.Name),
                new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression(ForLoopInitStatement.Name),
                    CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));

            var IVariableReference = new CodeVariableReferenceExpression(ForLoopInitStatement.Name);
            var InternalListIndexer = new CodeIndexerExpression(InternalListFieldReference, IVariableReference);
            var InvokeAddStatement = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Add",
                InternalListIndexer);

            var ForLoopStatement = new CodeIterationStatement(ForLoopInitStatement, ForLoopTestExpression, ForLoopIncrementStatement);
            ForLoopStatement.Statements.Add(InvokeAddStatement);

            // Add statements.
            SetValuesMethod.Statements.Add(FormAssignStatement);
            SetValuesMethod.Statements.Add(XPathAssignStatement);
            SetValuesMethod.Statements.Add(IsNewAssignStatement);
            SetValuesMethod.Statements.Add(NewElementAssignStatement);
            SetValuesMethod.Statements.Add(InvokeClearStatement);
            SetValuesMethod.Statements.Add(ForLoopStatement);

            return SetValuesMethod;

        }

        private static CodeMemberMethod CreateThrowIfOutOfRangeMethod()
        {
            var ThrowIfOutOfRangeMethod = new CodeMemberMethod();
            ThrowIfOutOfRangeMethod.Name = "ThrowIfOutOfRange";
            ThrowIfOutOfRangeMethod.Attributes = MemberAttributes.Private;
            ThrowIfOutOfRangeMethod.Parameters.Add(IndexParameterDeclaration);

            var IndexGreaterThanCountCondition = new CodeBinaryOperatorExpression(IndexArgumentReference,
                CodeBinaryOperatorType.GreaterThan, CountPropertyReference);

            var IndexIsLessThanOneCondition = new CodeBinaryOperatorExpression(IndexArgumentReference,
                CodeBinaryOperatorType.LessThan, new CodePrimitiveExpression(1));

            var OutOfRangeCondition = new CodeBinaryOperatorExpression(IndexGreaterThanCountCondition,
                CodeBinaryOperatorType.BooleanOr, IndexIsLessThanOneCondition);

            var ThrowArgumentOutOfRangeException = new CodeThrowExceptionStatement(
                new CodeObjectCreateExpression(new CodeTypeReference(typeof(ArgumentOutOfRangeException)),
                    new CodePrimitiveExpression("Index must be within the bounds of the List.")));

            var IfIndexOutOfRangeStatement = new CodeConditionStatement(OutOfRangeCondition);
            IfIndexOutOfRangeStatement.TrueStatements.Add(ThrowArgumentOutOfRangeException);

            ThrowIfOutOfRangeMethod.Statements.Add(IfIndexOutOfRangeStatement);

            return ThrowIfOutOfRangeMethod;
        }

        #endregion // Helper Methods.

        #region IList Members

        private static CodeMemberProperty CreateContainerCollectionIndexer()
        {
            // Generates the following code:

            // public T this[int index]
            // {
            //     get
            //     {
            //         ThrowIfOutOfRange();
            //         if (_IsNew)
            //         {
            //              return _InternalList[index - 1];
            //         }
            //         return (T)Activator.CreateInstance(typeof(T), _Form, _XPath, index);
            //     }
            //     set
            //     {
            //         ThrowIfOutOfRange();
            //         if (_IsNew)
            //         {
            //             _InternalList[index - 1] = value;
            //             return;
            //         }
            //         RepeatingContainerBase Container = ((RepeatingContainerBase)(((object)(item))));
            //         Container.SetValues(_Form, _XPath, index);
            //     }
            // }

            // Create property.
            var IndexedProperty = new CodeMemberProperty();
            IndexedProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            IndexedProperty.Name = "Item";
            IndexedProperty.HasGet = true;
            IndexedProperty.HasSet = false;
            IndexedProperty.Type = GenericTypeReference;
            IndexedProperty.Parameters.Add(IndexParameterDeclaration);

            // Indexed property getter.
            var CreateInstanceMethodReference = new CodeMethodReferenceExpression(
                new CodeTypeReferenceExpression(typeof(Activator).Name), "CreateInstance");
            var InvokeCreateInstance = new CodeMethodInvokeExpression(CreateInstanceMethodReference,
                new CodeTypeOfExpression(GenericTypeReference), ContextFieldReference, XPathFieldReference, IndexArgumentReference);
            var CastToGeneric = new CodeCastExpression(GenericTypeReference, InvokeCreateInstance);
            var ReturnStatement = new CodeMethodReturnStatement(CastToGeneric);

            // Create _InternalList indexer.
            var InternalListIndexerExpression = new CodeIndexerExpression(InternalListFieldReference, new CodeBinaryOperatorExpression(
                IndexArgumentReference, CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)));

            IndexedProperty.GetStatements.Add(CreateIfIsNewStatement(InternalListIndexerExpression, false));
            IndexedProperty.GetStatements.Add(InvokeThrowIfOutOfRange);
            IndexedProperty.GetStatements.Add(ReturnStatement);

            // Indexed property setter.  
            var RepeatingContainerType = new CodeTypeReference("RepeatingContainerBase");
            var CastItemAsObject = new CodeCastExpression(new CodeTypeReference(typeof(object)), 
                new CodePropertySetValueReferenceExpression());
            var CastItemAsRepeatingContainer = new CodeCastExpression(RepeatingContainerType, CastItemAsObject);
            var ContainerVariableDeclarationStatement = new CodeVariableDeclarationStatement(RepeatingContainerType, "Container",
                CastItemAsRepeatingContainer);
            var ContainerVariableReference = new CodeVariableReferenceExpression(ContainerVariableDeclarationStatement.Name);

            var InvokeSetValuesStatement = new CodeMethodInvokeExpression(ContainerVariableReference, "SetValues",
                ContextFieldReference, XPathFieldReference, IndexArgumentReference);

            // Create assign to _InternalList indexer.
            var InternalListAssignStatement = new CodeAssignStatement(InternalListIndexerExpression, 
                new CodePropertySetValueReferenceExpression());

            IndexedProperty.SetStatements.Add(CreateIfIsNewStatement(InternalListAssignStatement));
            IndexedProperty.SetStatements.Add(InvokeThrowIfOutOfRange);
            IndexedProperty.SetStatements.Add(ContainerVariableDeclarationStatement);
            IndexedProperty.SetStatements.Add(InvokeSetValuesStatement);

            return IndexedProperty;
        }

        private static CodeMemberProperty CreateCollectionIndexer()
        {
            // Generates the following code:

            // public T this[int index]
            // {
            //     get
            //     {
            //         ThrowIfOutOfRange();
            //         return NodeOperations.GetValue<T>(_FormContext, string.Format("{0}[{1}]", _XPath, index));
            //     }
            //     set
            //     {
            //         ThrowIfOutOfRange();
            //         XPathNavigator Element = NodeOperations.GetNavigator(_FormContext, string.Format("{0}[{1}]", _XPath, index));
            //         NodeOperations.SetValue(Element, value, _IsNillable);
            //     }
            // }

            // Create property.
            var IndexedProperty = new CodeMemberProperty();
            IndexedProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            IndexedProperty.Name = "Item";
            IndexedProperty.HasGet = true;
            IndexedProperty.HasSet = false;
            IndexedProperty.Type = GenericTypeReference;
            IndexedProperty.Parameters.Add(IndexParameterDeclaration);

            // Indexed property getter.
            var InvokeGetValue = new CodeMethodInvokeExpression(NodeOperationsReference,
                "GetValue", ContextFieldReference, CreateInvokeStringFormatExpression(IndexArgumentReference));
            InvokeGetValue.Method.TypeArguments.Add(GenericTypeReference);

            var ReturnStatement = new CodeMethodReturnStatement(InvokeGetValue);

            // Create _InternalList indexer.
            var InternalListIndexerExpression = new CodeIndexerExpression(InternalListFieldReference, new CodeBinaryOperatorExpression(
                IndexArgumentReference, CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)));

            IndexedProperty.GetStatements.Add(CreateIfIsNewStatement(InternalListIndexerExpression, false));
            IndexedProperty.GetStatements.Add(InvokeThrowIfOutOfRange);
            IndexedProperty.GetStatements.Add(ReturnStatement);

            // Indexed property setter.  
            var InvokeGetNavigatorByIndex = CreateInvokeNavigatorExpression(CreateInvokeStringFormatExpression(IndexArgumentReference));
            var FieldVariableDeclaration = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference, "Element", InvokeGetNavigatorByIndex);
            var FieldVariableReference = new CodeVariableReferenceExpression(FieldVariableDeclaration.Name);

            var InvokeSetValueStatement = new CodeMethodInvokeExpression(NodeOperationsReference, "SetValue",
                FieldVariableReference, new CodePropertySetValueReferenceExpression(),
                new CodeFieldReferenceExpression(null, "_IsNillable"));

            // Create assign to _InternalList indexer.
            var InternalListAssignStatement = new CodeAssignStatement(InternalListIndexerExpression,
                new CodePropertySetValueReferenceExpression());

            IndexedProperty.SetStatements.Add(CreateIfIsNewStatement(InternalListAssignStatement));
            IndexedProperty.SetStatements.Add(InvokeThrowIfOutOfRange);
            IndexedProperty.SetStatements.Add(FieldVariableDeclaration);
            IndexedProperty.SetStatements.Add(InvokeSetValueStatement);

            return IndexedProperty;
        }

        private static CodeMemberMethod CreateInsertMethod()
        {
            // Generates the following code:

            // public void Insert(int index, T item)
            // {
            //     if (_IsNew)
            //     {
            //         _InternalList.Insert(index - 1, item);
            //         return;
            //     }
            //     ThrowIfOutOfRange();
            //     if (index == 1)
            //     {
            //         XPathNavigator NextElement = NodeOperations.GetNavigator(string.Format("{0}[{1}]", _XPath, index));
            //         NextElement.InsertBefore(_NewElement);
            //     }
            //     else
            //     {
            //         XPathNavigator PreviousElement = NodeOperations.GetNavigator(string.Format("{0}[{1}]", _XPath, index - 1));
            //         PreviousElement.InsertAfter(_NewElement);
            //     }
            //     this[index] = item;
            // }

            // Create method.
            var InsertMethod = new CodeMemberMethod();
            InsertMethod.Name = "Insert";
            InsertMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            InsertMethod.Parameters.Add(IndexParameterDeclaration);
            InsertMethod.Parameters.Add(ItemParameterDeclaration);

            // If index == 1 statement.
            var IndexEqualsOneCondition = new CodeBinaryOperatorExpression(IndexArgumentReference, 
                CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(1));

            var IfIndexEqualsOneStatement = new CodeConditionStatement(IndexEqualsOneCondition);

            var NextElementVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference,
                "NextElement", CreateInvokeNavigatorExpression(CreateInvokeStringFormatExpression(IndexArgumentReference)));
            var NextElementVariableReference = new CodeVariableReferenceExpression(NextElementVariableDeclarationStatement.Name);

            var InvokeInsertBeforeStatement = new CodeMethodInvokeExpression(NextElementVariableReference, "InsertBefore",
                NewElementFieldReference);

            IfIndexEqualsOneStatement.TrueStatements.Add(NextElementVariableDeclarationStatement);
            IfIndexEqualsOneStatement.TrueStatements.Add(InvokeInsertBeforeStatement);

            var PreviousElementVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference,
                "PreviousElement", CreateInvokeNavigatorExpression(CreateInvokeStringFormatExpression(
                new CodeBinaryOperatorExpression(IndexArgumentReference, CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(1)))));
            var PreviousElementVariableReference = new CodeVariableReferenceExpression(PreviousElementVariableDeclarationStatement.Name);

            var InvokeInsertAfterStatement = new CodeMethodInvokeExpression(PreviousElementVariableReference, "InsertAfter",
                NewElementFieldReference);

            IfIndexEqualsOneStatement.FalseStatements.Add(PreviousElementVariableDeclarationStatement);
            IfIndexEqualsOneStatement.FalseStatements.Add(InvokeInsertAfterStatement);

            // this[index] = item assignment statment.
            var ThisIndexer = new CodeIndexerExpression(new CodeThisReferenceExpression(), IndexArgumentReference);
            var AssignStatement = new CodeAssignStatement(ThisIndexer, ItemArgumentReference);

            // Insert into _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Insert",
                new CodeBinaryOperatorExpression(IndexArgumentReference, CodeBinaryOperatorType.Subtract,
                    new CodePrimitiveExpression(1)), ItemArgumentReference);


            InsertMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, true));
            InsertMethod.Statements.Add(InvokeThrowIfOutOfRange);
            InsertMethod.Statements.Add(IfIndexEqualsOneStatement);
            InsertMethod.Statements.Add(AssignStatement);

            return InsertMethod;
        }

        private static CodeMemberMethod CreateRemoveAtMethod()
        {
            // Generates the following code:

            // public void RemoveAt(int index)
            // {
            //     if ((_IsNew == true))
            //     {
            //         return _InternalList.Remove(item);
            //     }
            //     ThrowIfOutOfRange();
            //     TryRemoveAt(index);
            // }

            // Create method.
            var RemoveAtMethod = new CodeMemberMethod();
            RemoveAtMethod.Name = "RemoveAt";
            RemoveAtMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            RemoveAtMethod.Parameters.Add(IndexParameterDeclaration);

            // Invoke TryRemoveAt statement.
            var TryRemoveAtMethodReference = new CodeMethodReferenceExpression(null, "TryRemoveAt");
            var InvokeTryRemoveAtStatement = new CodeMethodInvokeExpression(TryRemoveAtMethodReference, IndexArgumentReference);

            // RemoveAt from _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "RemoveAt",
                new CodeBinaryOperatorExpression(IndexArgumentReference, CodeBinaryOperatorType.Subtract,
                    new CodePrimitiveExpression(1)));

            RemoveAtMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, true));
            RemoveAtMethod.Statements.Add(InvokeThrowIfOutOfRange);
            RemoveAtMethod.Statements.Add(InvokeTryRemoveAtStatement);

            return RemoveAtMethod;
        }

        private static CodeMemberMethod CreateTryRemoveAtMethod()
        {
            // Generates the following code:

            // public bool TryRemoveAt(int index)
            // {
            //     XPathNavigator Element = NodeOperations.GetNavigator(string.Format("{0}[{1}]", _XPath, index));
            //     if (Element != null)
            //     {
            //         Element.DeleteSelf();
            //         return true;
            //     }
            //     return false;
            // }

            var TryRemoveAtMethod = new CodeMemberMethod();
            TryRemoveAtMethod.Name = "TryRemoveAt";
            TryRemoveAtMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            TryRemoveAtMethod.Parameters.Add(IndexParameterDeclaration);
            TryRemoveAtMethod.ReturnType = BooleanTypeReference;

            // Element variable declaration.
            var InvokeGetNavigatorByIndex = CreateInvokeNavigatorExpression(CreateInvokeStringFormatExpression(IndexArgumentReference));
            var ElementVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference, "Element",
                InvokeGetNavigatorByIndex);
            var ElementVariableReference = new CodeVariableReferenceExpression(ElementVariableDeclarationStatement.Name);

            // If Element != null statement.
            var ElementNotNullCondition = new CodeBinaryOperatorExpression(ElementVariableReference,
                CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
            var InvokeDeleteSelfStatement = new CodeMethodInvokeExpression(ElementVariableReference, "DeleteSelf");
            var ReturnTrueStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(true));
            var IfElementNotNullStatement = new CodeConditionStatement(ElementNotNullCondition);
                IfElementNotNullStatement.TrueStatements.Add(InvokeDeleteSelfStatement);
                IfElementNotNullStatement.TrueStatements.Add(ReturnTrueStatement);

            // Return false statement;
            var ReturnFalseStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(false));

            TryRemoveAtMethod.Statements.Add(ElementVariableDeclarationStatement);
            TryRemoveAtMethod.Statements.Add(IfElementNotNullStatement);
            TryRemoveAtMethod.Statements.Add(ReturnFalseStatement);

            return TryRemoveAtMethod;
        }

        private static CodeMemberMethod CreateIndexOfMethod()
        {
            // Generates the following code:

            // public int IndexOf(T item)
            // {
            //     if (_IsNew)
            //     {
            //         return _InternalList.IndexOf(item);
            //     }
            //     int Index = 1;
            //     IEnumerator<T> Items = GetEnumerator();
            //     while (Items.MoveNext())
            //     {
            //         if (Items.Current.Equals(item))
            //         {
            //             return Index;
            //         }
            //         Index++;
            //     }
            //     return -1;
            // }

            // Create method.
            var IndexOfMethod = new CodeMemberMethod();
            IndexOfMethod.Name = "IndexOf";
            IndexOfMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            IndexOfMethod.ReturnType = IntegerTypeReference;
            IndexOfMethod.Parameters.Add(ItemParameterDeclaration);

            // Index variable declaration statement.
            var IndexVariableDeclarationStatement = new CodeVariableDeclarationStatement(IntegerTypeReference,
                "Index", new CodePrimitiveExpression(1));
            var IndexVariableReference = new CodeVariableReferenceExpression(IndexVariableDeclarationStatement.Name);

            // Items variable declaration statement.
            var InvokeGetEnumerator = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "GetEnumerator"));
            var ItemsVariableDeclarationStatement = new CodeVariableDeclarationStatement(GenericIEnumeratorTypeReference,
                "Items", InvokeGetEnumerator);
            var ItemsVariableReference = new CodeVariableReferenceExpression(ItemsVariableDeclarationStatement.Name);

            // While loop statement.
            var InvokeMoveNext = new CodeMethodInvokeExpression(ItemsVariableReference, "MoveNext");

            // While loop if statement.
            var ItemArgumentReference = new CodeArgumentReferenceExpression("item");
            var ItemsCurrentPropertyReference = new CodePropertyReferenceExpression(ItemsVariableReference, "Current");
            var InvokeItemEquals = new CodeMethodInvokeExpression(ItemsCurrentPropertyReference, "Equals", ItemArgumentReference);
            var ReturnIndexStatement = new CodeMethodReturnStatement(IndexVariableReference);
            var IfItemsEqualStatement = new CodeConditionStatement(InvokeItemEquals, ReturnIndexStatement);

            var IndexIncrementStatement = new CodeAssignStatement(IndexVariableReference, new CodeBinaryOperatorExpression(
                IndexVariableReference, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));

            var WhileLoopStatement = new CodeIterationStatement(new CodeSnippetStatement(), InvokeMoveNext, new CodeSnippetStatement(), IfItemsEqualStatement, IndexIncrementStatement);

            // Return -1 statement.
            var ReturnNegativeOneStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(-1));

            // Create _InternalList remove statement.
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "IndexOf", ItemArgumentReference);

            // Add statements to method.
            IndexOfMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, false));
            IndexOfMethod.Statements.Add(IndexVariableDeclarationStatement);
            IndexOfMethod.Statements.Add(ItemsVariableDeclarationStatement);
            IndexOfMethod.Statements.Add(WhileLoopStatement);
            IndexOfMethod.Statements.Add(ReturnNegativeOneStatement);
            
            return IndexOfMethod;
        }

        private static CodeMemberMethod CreateContainerIndexOfMethod()
        {
            // Generates the following code:

            // public int IndexOf(T item)
            // {
            //     if (_IsNew)
            //     {
            //         return _InternalList.IndexOf(item);
            //     }
            //     return ((RepeatingContainerBase)(object)item).Index;
            // }

            // Create method.
            var IndexOfMethod = new CodeMemberMethod();
            IndexOfMethod.Name = "IndexOf";
            IndexOfMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            IndexOfMethod.ReturnType = IntegerTypeReference;
            IndexOfMethod.Parameters.Add(ItemParameterDeclaration);

            // Item variable declaration statement.
            var CastItemAsObject = new CodeCastExpression(new CodeTypeReference(typeof(object)), ItemArgumentReference);
            var CastItemAsRepeatingContainer = new CodeCastExpression(
                new CodeTypeReference("RepeatingContainerBase"), CastItemAsObject);
            var IndexPropertyReference = new CodePropertyReferenceExpression(CastItemAsRepeatingContainer, "Index");
            var ReturnStatement = new CodeMethodReturnStatement(IndexPropertyReference);

            // Create _InternalList remove statement.
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "IndexOf", ItemArgumentReference);

            // Add statements to method.
            IndexOfMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, false));
            IndexOfMethod.Statements.Add(ReturnStatement);

            return IndexOfMethod;
        }

        #endregion // IList Members.

        #region ICollection Members

        private static CodeMemberMethod CreateAddMethod()
        {
            // Generates the following code:

            // public void Add(T item)
            // {
            //     if (_IsNew)
            //     {
            //         _InternalList.Add(item);
            //         return;
            //     }
            //     Add();
            //     this[Count] = item;
            // }

            // Create method.
            var AddMethod = new CodeMemberMethod();
            AddMethod.Name = "Add";
            AddMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            AddMethod.Parameters.Add(ItemParameterDeclaration);

            // Invoke Add statement.
            var InvokeAddStatement = new CodeMethodInvokeExpression(null, "Add");

            // this[Count] = item assignment statment.
            var ThisIndexer = new CodeIndexerExpression(new CodeThisReferenceExpression(), CountPropertyReference);
            var AssignStatement = new CodeAssignStatement(ThisIndexer, ItemArgumentReference);

            // Add to _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Add", ItemArgumentReference);

            AddMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, true));
            AddMethod.Statements.Add(InvokeAddStatement);
            AddMethod.Statements.Add(AssignStatement);

            return AddMethod;
        }

        private static CodeMemberMethod CreateInternalAddMethod()
        {
            // Generates the following code:

            // public void Add()
            // {
            //     if (Count == 0)
            //     {
            //         InsertFirstElement(_XPath, _NewElement);
            //     }
            //     else
            //     {
            //         XPathNavigator LastElement = NodeOperations.GetNavigator(string.Format("{0}[last()]", _XPath));
            //         LastElement.InsertAfter(_NewElement);
            //     }
            // }

            // Create method.
            var AddMethod = new CodeMemberMethod();
            AddMethod.Name = "InternalAdd";
            AddMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // If index == 1 statement.
            var CountEqualsZeroCondition = new CodeBinaryOperatorExpression(CountPropertyReference,
                CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(0));

            var IfCountEqualsZeroStatement = new CodeConditionStatement(CountEqualsZeroCondition);

            var InvokeInsertFirstElement = new CodeMethodInvokeExpression(null, "InsertFirstElement", XPathFieldReference,
                NewElementFieldReference);

            IfCountEqualsZeroStatement.TrueStatements.Add(InvokeInsertFirstElement);
            var InvokeStringFormat = new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression(StringTypeReference), "Format", 
                new CodePrimitiveExpression("{0}[last()]"), XPathFieldReference);
            var LastElementVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference,
                "LastElement", CreateInvokeNavigatorExpression(InvokeStringFormat));
            var LastElementVariableReference = new CodeVariableReferenceExpression(LastElementVariableDeclarationStatement.Name);

            var InvokeInsertAfterStatement = new CodeMethodInvokeExpression(LastElementVariableReference, "InsertAfter",
                NewElementFieldReference);

            IfCountEqualsZeroStatement.FalseStatements.Add(LastElementVariableDeclarationStatement);
            IfCountEqualsZeroStatement.FalseStatements.Add(InvokeInsertAfterStatement);

            AddMethod.Statements.Add(IfCountEqualsZeroStatement);

            return AddMethod;
        }

        private static CodeMemberMethod CreateAddEmptyMethod()
        {
            // Generates the following code:

            // public void Add()
            // {
            //     if ((_IsNew == true))
            //     {
            //         _InternalList.Add(default(T));
            //         return;
            //     }
            //     InternalAdd();
            // }

            // Create method.
            var AddMethod = new CodeMemberMethod();
            AddMethod.Name = "Add";
            AddMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // Invoke Add statement.
            var InvokeInternalAddStatement = new CodeMethodInvokeExpression(null, "InternalAdd");

            // Add to _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Add", 
                new CodeDefaultValueExpression(GenericTypeReference));

            AddMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, true));
            AddMethod.Statements.Add(InvokeInternalAddStatement);

            return AddMethod;
        }

        private static CodeMemberMethod CreateContainerAddEmptyMethod()
        {
            // Generates the following code:

            // public void Add()
            // {
            //     if ((_IsNew == true))
            //     {
            //         _InternalList.Add((T)(Activator.CreateInstance(typeof(T))));
            //         return;
            //     }
            //     InternalAdd();
            // }

            // Create method.
            var AddMethod = new CodeMemberMethod();
            AddMethod.Name = "Add";
            AddMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // Invoke Add statement.
            var InvokeInternalAddStatement = new CodeMethodInvokeExpression(null, "InternalAdd");

            // Invoke Activator.CreateInstance()
            var CreateInstanceMethodReference = new CodeMethodReferenceExpression(
                new CodeTypeReferenceExpression(typeof(Activator).Name), "CreateInstance");
            var InvokeCreateInstance = new CodeMethodInvokeExpression(CreateInstanceMethodReference,
                new CodeTypeOfExpression(GenericTypeReference));
            var CastToGeneric = new CodeCastExpression(GenericTypeReference, InvokeCreateInstance);

            // Add to _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Add",
                CastToGeneric);

            AddMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, true));
            AddMethod.Statements.Add(InvokeInternalAddStatement);

            return AddMethod;
        }

        private static CodeMemberMethod CreateClearMethod()
        {
            // Generates the following code:

            // public void Clear()
            // {
            //     Clear(Helpers.RowSelectionOption.All);
            // }

            // Create method.
            var ClearMethod = new CodeMemberMethod();
            ClearMethod.Name = "Clear";
            ClearMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // Return statement.
            var RowSelectionOptionEnumReference = new CodeTypeReferenceExpression(typeof(RowSelectionOption));
            var RowSelectionOptionAllReference = new CodeFieldReferenceExpression(RowSelectionOptionEnumReference, "All");
            var InvokeClear = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "Clear",
                RowSelectionOptionAllReference);

            // Clear _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Clear");

            ClearMethod.Statements.Add(InvokeClear);

            return ClearMethod;
        }

        private static CodeMemberMethod CreateClearOverloadMethod()
        {
            // Generates the following code:

            // public void Clear(Helpers.RowSelectionOptions rowSelectionOptions, params string[] tablefields)
            // {
            //     if (_IsNew)
            //     {
            //         _InternalList.Clear();
            //         return;
            //     }
            //     RepeatingTableHelper.Clear(_FormContext, _XPath,
            //         rowSelectionOptions, tableFields);
            // }

            // Create method.
            var ClearMethod = new CodeMemberMethod();
            ClearMethod.Name = "Clear";
            ClearMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // Add parameters.
            var RowSelectionOptionsParameter = new CodeParameterDeclarationExpression(typeof(RowSelectionOption), 
                "rowSelectionOptions");
            var RowSelectionOptionsArgument = new CodeArgumentReferenceExpression(RowSelectionOptionsParameter.Name);
            var TableFieldsParameter = new CodeParameterDeclarationExpression(typeof(string[]),
                "tableFields");
            TableFieldsParameter.CustomAttributes.Add(
                new CodeAttributeDeclaration(new CodeTypeReference(typeof(ParamArrayAttribute))));
            var TableFieldsArgument = new CodeArgumentReferenceExpression(TableFieldsParameter.Name);

            ClearMethod.Parameters.Add(RowSelectionOptionsParameter);
            ClearMethod.Parameters.Add(TableFieldsParameter);

            // Return statement.
            var HelpersTypeReference = new CodeTypeReferenceExpression(typeof(RepeatingTableHelpers).Name);
            var ClearRepeatingTableMethodReference = new CodeMethodReferenceExpression(HelpersTypeReference, "Clear");
            var InvokeClearRepeatingTable = new CodeMethodInvokeExpression(ClearRepeatingTableMethodReference,
                ContextFieldReference, XPathFieldReference, RowSelectionOptionsArgument, TableFieldsArgument);

            // Clear _InternalList;
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Clear");

            ClearMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, true));
            ClearMethod.Statements.Add(InvokeClearRepeatingTable);

            return ClearMethod;
        }

        private static CodeMemberMethod CreateContainsMethod()
        {
            // Generates the following code:

            // public bool Contains(T item)
            // {
            //     if (_IsNew)
            //     {
            //         return _InternalList.Contains(item);
            //     }
            //     IEnumerator<T> Items = GetEnumerator();
            //     for (;Items.MoveNext();)
            //     {
            //         if (Items.Current.Equals(item))
            //         {
            //             return true;
            //         }
            //     }
            //     return false;
            // }

            // Create method.
            var ContainsMethod = new CodeMemberMethod();
            ContainsMethod.Name = "Contains";
            ContainsMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            ContainsMethod.ReturnType = BooleanTypeReference;
            ContainsMethod.Parameters.Add(ItemParameterDeclaration);

            // Items variable declaration statement.
            var InvokeGetEnumerator = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "GetEnumerator"));
            var ItemsVariableDeclarationStatement = new CodeVariableDeclarationStatement(GenericIEnumeratorTypeReference,
                "Items", InvokeGetEnumerator);
            var ItemsVariableReference = new CodeVariableReferenceExpression(ItemsVariableDeclarationStatement.Name);

            // While loop statement.
            var InvokeMoveNext = new CodeMethodInvokeExpression(ItemsVariableReference, "MoveNext");

            // While loop if statement.
            var ItemsCurrentPropertyReference = new CodePropertyReferenceExpression(ItemsVariableReference, "Current");
            var InvokeItemEquals = new CodeMethodInvokeExpression(ItemsCurrentPropertyReference, "Equals", ItemArgumentReference);
            var ReturnTrueStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(true));
            var IfItemsEqualStatement = new CodeConditionStatement(InvokeItemEquals, ReturnTrueStatement);

            var WhileLoopStatement = new CodeIterationStatement(new CodeSnippetStatement(), InvokeMoveNext, new CodeSnippetStatement(), IfItemsEqualStatement);

            // Return false statement.
            var ReturnFalseStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(false));

            // Create _InternalList contains statement.
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Contains", ItemArgumentReference);

            // Add statements to method.
            ContainsMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, false));
            ContainsMethod.Statements.Add(ItemsVariableDeclarationStatement);
            ContainsMethod.Statements.Add(WhileLoopStatement);
            ContainsMethod.Statements.Add(ReturnFalseStatement);

            return ContainsMethod;
        }

        private static CodeMemberMethod CreateContainerContainsMethod()
        {
            // Generates the following code:

            // public bool Contains(T item)
            // {
            //     if (_IsNew)
            //     {
            //         return _InternalList.Contains(item);
            //     }
            //     int Index = IndexOf(item);
            //     return  Index != 0 && Index <= Count;
            // }

            // Create method.
            var ContainsMethod = new CodeMemberMethod();
            ContainsMethod.Name = "Contains";
            ContainsMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            ContainsMethod.ReturnType = BooleanTypeReference;
            ContainsMethod.Parameters.Add(ItemParameterDeclaration);

            // Index variable declaration statement.
            var InvokeIndexOf = new CodeMethodInvokeExpression(null, "IndexOf", ItemArgumentReference);
            var IndexVariableDeclarationStatement = new CodeVariableDeclarationStatement(IntegerTypeReference,
                "Index", InvokeIndexOf);
            var IndexVariableReference = new CodeVariableReferenceExpression(IndexVariableDeclarationStatement.Name);

            var IndexNotZeroExpression = new CodeBinaryOperatorExpression(IndexVariableReference,
                CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(0));
            var IndexLessThanOrEqualsCountExpression = new CodeBinaryOperatorExpression(IndexVariableReference,
                CodeBinaryOperatorType.LessThanOrEqual, CountPropertyReference);
            
            var ReturnStatement = new CodeMethodReturnStatement(new CodeBinaryOperatorExpression(IndexNotZeroExpression,
                CodeBinaryOperatorType.BooleanAnd, IndexLessThanOrEqualsCountExpression));

            // Create _InternalList contains statement.
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Contains", ItemArgumentReference);

            // Add statements to method.
            ContainsMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, false));
            ContainsMethod.Statements.Add(IndexVariableDeclarationStatement);
            ContainsMethod.Statements.Add(ReturnStatement);

            return ContainsMethod;
        }

        private static CodeMemberMethod CreateCopyToMethod()
        {
            // Generates the following code:

            // public void CopyTo(int[] array, int arrayIndex)
            // {
            //     if(array == null)
            //     {
            //         throw new ArgumentNullException("array");
            //     }
            //     if(arrayIndex < 0 || arrayIndex >= array.Length)
            //     {
            //         throw new ArgumentOutOfRangeException("index");
            //     }
            //     if(array.Length - arrayIndex < Count)
            //     {
            //         throw new ArgumentException("The number of elements in the source ICollection is greater than the available space from index to the end of the destination array.");
            //     }
            //
            //     XPathNodeIterator Nodes = _Form.MainDataSource.CreateNavigator().Select(_XPath, _Form.NamespaceManager);
            //     for(int i = arrayIndex; i < array.Length; i++)
            //     {
            //         int Item;
            //         NodeOperations.TryGetValue(Nodes.Current, out Item);
            //         array[i] = Item;
            //         Nodes.MoveNext();
            //     }
            // }

            // Create method.
            var CopyToMethod = new CodeMemberMethod();
            CopyToMethod.Name = "CopyTo";
            CopyToMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            CopyToMethod.Parameters.Add(new CodeParameterDeclarationExpression("T[]", "array"));
            CopyToMethod.Parameters.Add(new CodeParameterDeclarationExpression(IntegerTypeReference, "arrayIndex"));

            var ThrowNotImplementedException = new CodeThrowExceptionStatement(
                new CodeObjectCreateExpression(new CodeTypeReference(typeof(NotImplementedException))));

            CopyToMethod.Statements.Add(ThrowNotImplementedException);

            return CopyToMethod;
        }

        private static CodeMemberMethod CreateRemoveMethod()
        {
            // Generates the following code:

            // public bool Remove(T item)
            // {
            //     if (_IsNew)
            //     {
            //        return _InternalList.Remove(item);
            //     }
            //     return TryRemoveAt(IndexOf(item));
            // }

            var RemoveMethod = new CodeMemberMethod();
            RemoveMethod.Name = "Remove";
            RemoveMethod.ReturnType = BooleanTypeReference;
            RemoveMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            RemoveMethod.Parameters.Add(ItemParameterDeclaration);

            // Return statement.
            var IndexOfMethodReference = new CodeMethodReferenceExpression(null, "IndexOf");
            var TryRemoveAtMethodReference = new CodeMethodReferenceExpression(null, "TryRemoveAt");
            var InvokeIndexOf = new CodeMethodInvokeExpression(IndexOfMethodReference, ItemArgumentReference);
            var InvokeTryRemoveAt = new CodeMethodInvokeExpression(TryRemoveAtMethodReference, InvokeIndexOf);
            var ReturnStatement = new CodeMethodReturnStatement(InvokeTryRemoveAt);

            // Create _InternalList remove statement.
            var InvokeExpression = new CodeMethodInvokeExpression(InternalListFieldReference, "Remove", ItemArgumentReference);

            // Add statements to method.
            RemoveMethod.Statements.Add(CreateIfIsNewStatement(InvokeExpression, false));
            RemoveMethod.Statements.Add(ReturnStatement);

            return RemoveMethod;
        }

        private static CodeMemberProperty CreateCountProperty()
        {
            // Generates the following code:

            // public int Count
            // {
            //     get
            //     {
            //         if (_IsNew)
            //         {
            //             return _InternalList.Count;
            //         }
            //         return _FormContext.Select(_XPath).Count;
            //     }
            // } 

            var CountProperty = new CodeMemberProperty();
            CountProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            CountProperty.Name = "Count";
            CountProperty.HasGet = true;
            CountProperty.Type = new CodeTypeReference(typeof(int));

            var SelectionCountReference = new CodePropertyReferenceExpression(InvokeSelectXPath, "Count");
            var CountReturnStatement = new CodeMethodReturnStatement(SelectionCountReference);

            // Create _InternalList remove statement.
            var PropertyReference = new CodePropertyReferenceExpression(InternalListFieldReference, "Count");

            // Add statements to method.
            CountProperty.GetStatements.Add(CreateIfIsNewStatement(PropertyReference, false));
            CountProperty.GetStatements.Add(CountReturnStatement);

            return CountProperty;
        }

        private static CodeMemberProperty CreateIsReadOnlyProperty()
        {
            // Generates the following code:

            // public bool IsReadOnly
            // {
            //     get { return _IsReadOnly; }
            //     private set { _IsReadOnly = value; }
            // }

            var IsReadOnlyProperty = new CodeMemberProperty();
            IsReadOnlyProperty.Name = "IsReadOnly";
            IsReadOnlyProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            IsReadOnlyProperty.Type = BooleanTypeReference;
            IsReadOnlyProperty.HasGet = true;

            // Get method.
            var ReturnStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(false));

            IsReadOnlyProperty.GetStatements.Add(ReturnStatement);

            return IsReadOnlyProperty;
        }

        private static CodeMemberField CreateIsNillableField()
        {
            // Generates the following code:

            // private bool _IsReadOnly;

            var IsReadOnlyField = new CodeMemberField(BooleanTypeReference, "_IsNillable");
            IsReadOnlyField.Attributes = MemberAttributes.Private;
            return IsReadOnlyField;
        }

        #endregion // ICollection Members.

        #region IEnumerable Members

        private static CodeMemberMethod CreateGetEnumeratorMethod()
        {
            // Generates the following code:

            // System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            // {
            //     return this.GetEnumerator();
            // }

            // Create method.
            var GetEnumeratorMethod = new CodeMemberMethod();
            GetEnumeratorMethod.Name = "System.Collections.IEnumerable.GetEnumerator";
            GetEnumeratorMethod.Attributes = MemberAttributes.Final;
            GetEnumeratorMethod.ReturnType = new CodeTypeReference(typeof(System.Collections.IEnumerator));

            // Return statement.

            var GetEnumeratorMethodReference = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), "GetEnumerator");
            var InvokeGetEnumerator = new CodeMethodInvokeExpression(GetEnumeratorMethodReference);
            var ReturnStatement = new CodeMethodReturnStatement(InvokeGetEnumerator);

            GetEnumeratorMethod.Statements.Add(ReturnStatement);

            return GetEnumeratorMethod;

        }

        private static CodeMemberMethod CreateGetEnumeratorTMethod()
        {
            // Generates the following code:

            // public IEnumerator<T> GetEnumerator()
            // {
            //     int Count = this.Count;
            //     for (int i = 0; i < Count; i++)
            //     {
            //         yield return this[i+1];
            //     }
            // }

            // Create the method.
            var GetEnumeratorMethod = new CodeMemberMethod();
            GetEnumeratorMethod.Name = "GetEnumerator";
            GetEnumeratorMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            GetEnumeratorMethod.ReturnType = GenericIEnumeratorTypeReference;

            // Count variable declaration statement.
            var CountVariableDeclarationStatement = new CodeVariableDeclarationStatement(IntegerTypeReference, "Count",
                CountPropertyReference);
            var CountVariableReference = new CodeVariableReferenceExpression(CountVariableDeclarationStatement.Name);

            // For loop statement.
            var ForLoopInitStatement = new CodeVariableDeclarationStatement(IntegerTypeReference, "i", new CodePrimitiveExpression(0));
            var ForLoopTestExpression = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression(ForLoopInitStatement.Name),
                CodeBinaryOperatorType.LessThan, CountVariableReference);
            var ForLoopIncrementStatement = new CodeAssignStatement(new CodeVariableReferenceExpression(ForLoopInitStatement.Name),
                new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression(ForLoopInitStatement.Name),
                    CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));

            // No yield keyword in CodeDom so it has to be a snippet.
            var YieldReturnStatment = new CodeSnippetExpression("yield return this[i+1];");

            var ForLoopStatement = new CodeIterationStatement(ForLoopInitStatement, ForLoopTestExpression, ForLoopIncrementStatement);
            ForLoopStatement.Statements.Add(YieldReturnStatment);

            GetEnumeratorMethod.Statements.Add(CountVariableDeclarationStatement);
            GetEnumeratorMethod.Statements.Add(ForLoopStatement);

            return GetEnumeratorMethod;
        }

        #endregion // IEnumerable Members.

        #region Custom Members

        private static CodeMemberMethod CreateFindNavigatorMethod()
        {
            // Generates the following code:

            // public T Find(XPathNavigator node)
            // {
            //     string XPath = InfoPathHelpers.FormInfo.GetFullXPath(node);
            //     if ((XPath.Contains(_XPath) == false))
            //     {
            //         return default(T);
            //     }
            //     else
            //     {
            //         XPathNavigator Node = node.Clone();
            //         int ChildPosition = XPath.Replace(_XPath, "").Split('/').Length;
            //         for (int i = 1; (i < ChildPosition); i = (i + 1))
            //         {
            //             Node.MoveToParent();
            //         }
            //         string Expression = string.Format("count(preceding-sibling::{0})+1", Node.Name);
            //         int Position = ((int)(((double)(Node.Evaluate(Expression, _Context.NamespaceManager)))));
            //         return this[Position];
            //     }
            // }

            // Create method.
            var FindMethod = new CodeMemberMethod();
            FindMethod.Name = "Find";
            FindMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            FindMethod.ReturnType = GenericTypeReference;

            // node parameter declaration.
            var NodeParameter = new CodeParameterDeclarationExpression(XPathNavigatorTypeReference, "node");
            var NodeArgument = new CodeArgumentReferenceExpression(NodeParameter.Name);
            FindMethod.Parameters.Add(NodeParameter);

            // XPath varaiable declaration statement.
            var InvokeGetFullXPath = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(FormInfo)),
                "GetFullXPath", NodeArgument);
            var XPathVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "XPath",
                InvokeGetFullXPath);
            var XPathVariableReference = new CodeVariableReferenceExpression(XPathVariableDeclarationStatement.Name);

            // If Node == null statement.
            var InvokeContainsMethod = new CodeMethodInvokeExpression(XPathVariableReference, "Contains", XPathFieldReference);
            var NotXPathContainsParentXPathCondition = new CodeBinaryOperatorExpression(InvokeContainsMethod,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false));
            var IfXPathContainsParentXPathStatement = new CodeConditionStatement(NotXPathContainsParentXPathCondition);

            // True statements.
            var ReturnDefaultStatement = new CodeMethodReturnStatement(new CodeDefaultValueExpression(GenericTypeReference));
            IfXPathContainsParentXPathStatement.TrueStatements.Add(ReturnDefaultStatement);

            // False statements.

            // Node variable declaration statement.
            var InvokeClone = new CodeMethodInvokeExpression(NodeArgument, "Clone");
            var NodeVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference, "Node",
                InvokeClone);
            var NodeVariableReference = new CodeVariableReferenceExpression(NodeVariableDeclarationStatement.Name);

            // ChildPosition variable declaration statement.
            var InvokeReplace = new CodeMethodInvokeExpression(XPathVariableReference, "Replace", XPathFieldReference,
                new CodePrimitiveExpression(string.Empty));
            var InvokeSplit = new CodeMethodInvokeExpression(InvokeReplace, "Split", new CodePrimitiveExpression('/'));
            var LengthProperty = new CodePropertyReferenceExpression(InvokeSplit, "Length");
            var ChildPositionVariableDeclarationStatement = new CodeVariableDeclarationStatement(IntegerTypeReference,
                "ChildPosition", LengthProperty);
            var ChildPositionVariableReference = new CodeVariableReferenceExpression(ChildPositionVariableDeclarationStatement.Name);
            
            // For statement.
            var InitStatement = new CodeVariableDeclarationStatement(IntegerTypeReference, "i", new CodePrimitiveExpression(1));
            var IVariableReference = new CodeVariableReferenceExpression(InitStatement.Name);
            var TestExpression = new CodeBinaryOperatorExpression(IVariableReference,
                CodeBinaryOperatorType.LessThan, ChildPositionVariableReference);
            var IncrementStatement = new CodeAssignStatement(IVariableReference, new CodeBinaryOperatorExpression(
                IVariableReference, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));
            var InvokeMoveToParent = new CodeMethodInvokeExpression(NodeVariableReference, "MoveToParent");

            var ForLoopStatement = new CodeIterationStatement(InitStatement, TestExpression, IncrementStatement);
            ForLoopStatement.Statements.Add(InvokeMoveToParent);

            // Expression variable declaration statement.
            var StringFormat = new CodePrimitiveExpression("count(preceding-sibling::{0})+1");
            var NodeNameProperty = new CodePropertyReferenceExpression(NodeVariableReference, "Name");
            var InvokeStringFormat = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                "Format", StringFormat, NodeNameProperty);
            var ExpressionVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference,
                "Expression", InvokeStringFormat);
            var ExpressionVariableReference = new CodeVariableReferenceExpression(ExpressionVariableDeclarationStatement.Name);

            // Position variable declaration statement.
            var NamespaceManagerProperty = new CodePropertyReferenceExpression(ContextFieldReference, "NamespaceManager");
            var InvokeEvaluate = new CodeMethodInvokeExpression(NodeVariableReference, "Evaluate",
                ExpressionVariableReference, NamespaceManagerProperty);
            var CastToDouble = new CodeCastExpression(typeof(double), InvokeEvaluate);
            var CastToInt = new CodeCastExpression(typeof(int), CastToDouble);
            var PosistionVariableDeclarationStatement = new CodeVariableDeclarationStatement(IntegerTypeReference,
                "Position", CastToInt);
            var PositionVariableReference = new CodeVariableReferenceExpression(PosistionVariableDeclarationStatement.Name);

            // Return statement.
            var ReturnStatement = new CodeMethodReturnStatement(new CodeIndexerExpression(new CodeThisReferenceExpression(),
                PositionVariableReference));

            IfXPathContainsParentXPathStatement.FalseStatements.Add(NodeVariableDeclarationStatement);
            IfXPathContainsParentXPathStatement.FalseStatements.Add(ChildPositionVariableDeclarationStatement);
            IfXPathContainsParentXPathStatement.FalseStatements.Add(ForLoopStatement);
            IfXPathContainsParentXPathStatement.FalseStatements.Add(ExpressionVariableDeclarationStatement);
            IfXPathContainsParentXPathStatement.FalseStatements.Add(PosistionVariableDeclarationStatement);
            IfXPathContainsParentXPathStatement.FalseStatements.Add(ReturnStatement);

            // Add statements to method.
            FindMethod.Statements.Add(XPathVariableDeclarationStatement);
            FindMethod.Statements.Add(IfXPathContainsParentXPathStatement);

            return FindMethod;
        }

        private static CodeMemberMethod CreateFindStringMethod()
        {
            // Generates the following code:

            // public T Find(string xPathFilter)
            // {
            //     string XPath = string.Format("{0}[{1}]", RemoveLastIndexFromXPath(_XPath), xPathFilter);
            //     XPathNavigator Node = NodeOperations.GetNavigator(XPath);
            //
            //     if (Node == null)
            //     {
            //         return default(T);
            //     }
            //     else
            //     {
            //         return Find(Node);
            //     }
            // }

            // Create method.
            var FindMethod = new CodeMemberMethod();
            FindMethod.Name = "Find";
            FindMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            FindMethod.ReturnType = GenericTypeReference;

            // xPathFilter parameter declaration.
            var XPathFilterParameter = new CodeParameterDeclarationExpression(StringTypeReference, "xPathFilter");
            var XPathFilterArgument = new CodeArgumentReferenceExpression(XPathFilterParameter.Name);
            FindMethod.Parameters.Add(XPathFilterParameter);

            // XPath varaiable declaration statement.
            var InvokeRemoveLastIndex = new CodeMethodInvokeExpression(null, "RemoveLastIndexFromXPath", XPathFieldReference);
            var InvokeStringFormat = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                "Format", new CodePrimitiveExpression("{0}[{1}]"), InvokeRemoveLastIndex, XPathFilterArgument);
            var XPathVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "XPath",
                InvokeStringFormat);
            var XPathVariableReference = new CodeVariableReferenceExpression(XPathVariableDeclarationStatement.Name);

            // Node variable declaration statement.
            var InvokeGetNavigator = CreateInvokeNavigatorExpression(XPathVariableReference);
            var NodeVariableDeclarationStatement = new CodeVariableDeclarationStatement(XPathNavigatorTypeReference, "Node",
                InvokeGetNavigator);
            var NodeVariableReference = new CodeVariableReferenceExpression(NodeVariableDeclarationStatement.Name);

            // If Node == null statement.
            var NodeEqualsNullCondition = new CodeBinaryOperatorExpression(NodeVariableReference,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(null));
            var ReturnDefaultStatement = new CodeMethodReturnStatement(new CodeDefaultValueExpression(GenericTypeReference));
            var InvokeFindMethod = new CodeMethodInvokeExpression(null, "Find", NodeVariableReference);
            var ReturnFindStatement = new CodeMethodReturnStatement(InvokeFindMethod);

            var IfNodeEqualsNullStatement = new CodeConditionStatement(NodeEqualsNullCondition);
            IfNodeEqualsNullStatement.TrueStatements.Add(ReturnDefaultStatement);
            IfNodeEqualsNullStatement.FalseStatements.Add(ReturnFindStatement);


            // Add statements to method.
            FindMethod.Statements.Add(XPathVariableDeclarationStatement);
            FindMethod.Statements.Add(NodeVariableDeclarationStatement);
            FindMethod.Statements.Add(IfNodeEqualsNullStatement);

            return FindMethod;
        }

        private static CodeMemberMethod CreateFindPredicateMethod()
        {
            // Generates the following code:

            // public T Find(Predicate<T> match)
            // {
            //     System.Collections.Generic.IEnumerator<T> Items = GetEnumerator();
            //     foreach (T Item in this)
            //     {
            //         if(match(Item))
            //         {
            //             return Item;
            //         }
            //     }
            //     return default(T);
            // }

            // Create method.
            var FindMethod = new CodeMemberMethod();
            FindMethod.Name = "Find";
            FindMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            FindMethod.ReturnType = GenericTypeReference;
            FindMethod.Parameters.Add(MatchParameterDeclaration);

            // Items variable declaration statement.
            var InvokeGetEnumerator = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "GetEnumerator"));
            var ItemsVariableDeclarationStatement = new CodeVariableDeclarationStatement(GenericIEnumeratorTypeReference,
                "Items", InvokeGetEnumerator);
            var ItemsVariableReference = new CodeVariableReferenceExpression(ItemsVariableDeclarationStatement.Name);

            // While loop test expression.
            var InvokeMoveNext = new CodeMethodInvokeExpression(ItemsVariableReference, "MoveNext");

            // While loop if statement.
            var ItemsCurrentPropertyReference = new CodePropertyReferenceExpression(ItemsVariableReference, "Current");
            var InvokeMatch = new CodeDelegateInvokeExpression(MatchArgumentReference, ItemsCurrentPropertyReference);
            var IsMatchCondition = new CodeBinaryOperatorExpression(InvokeMatch, 
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
            var ReturnItemStatement = new CodeMethodReturnStatement(ItemsCurrentPropertyReference);
            var IfItemIsMatchStatement = new CodeConditionStatement(IsMatchCondition, ReturnItemStatement);

            var WhileLoopStatement = new CodeIterationStatement(new CodeSnippetStatement(), InvokeMoveNext, new CodeSnippetStatement(), IfItemIsMatchStatement);

            // Return default(T) statement.
            var ReturnDefaultStatement = new CodeMethodReturnStatement(new CodeDefaultValueExpression(GenericTypeReference));

            // Add statements to method.
            FindMethod.Statements.Add(ItemsVariableDeclarationStatement);
            FindMethod.Statements.Add(WhileLoopStatement);
            FindMethod.Statements.Add(ReturnDefaultStatement);

            return FindMethod;
        }

        private static CodeMemberMethod CreateFindAllStringMethod()
        {
            // Generates the following code:

            // public List<T> FindAll(string xPathFilter)
            // {
            //     string XPath = string.Format("{0}[{1}]", RemoveIndexesFromXPath(_XPath), xPathFilter);
            //
            //     XPathNodeIterator Iterator = _FormContext.Select(XPath);
            //
            //     List<T> Results = new List<T>();
            //
            //     for (; Iterator.MoveNext(); )
            //     {
            //         Results.Add(Find(Iterator.Current));
            //     }
            //     return Results;
            // }

            // Create method.
            var FindAllMethod = new CodeMemberMethod();
            FindAllMethod.Name = "FindAll";
            FindAllMethod.ReturnType = new CodeTypeReference("List", GenericTypeReference);
            FindAllMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // xPathFilter parameter declaration.
            var XPathFilterParameter = new CodeParameterDeclarationExpression(StringTypeReference, "xPathFilter");
            var XPathFilterArgument = new CodeArgumentReferenceExpression(XPathFilterParameter.Name);
            FindAllMethod.Parameters.Add(XPathFilterParameter);

            // XPath varaiable declaration statement.
            var InvokeRemoveLastIndex = new CodeMethodInvokeExpression(null, "RemoveLastIndexFromXPath", XPathFieldReference);
            var InvokeStringFormat = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                "Format", new CodePrimitiveExpression("{0}[{1}]"), InvokeRemoveLastIndex, XPathFilterArgument);
            var XPathVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "XPath",
                InvokeStringFormat);
            var XPathVariableReference = new CodeVariableReferenceExpression(XPathVariableDeclarationStatement.Name);

            // Iterator variable declaration statement.
            var InvokeSelect = new CodeMethodInvokeExpression(ContextFieldReference, "Select", XPathVariableReference);
            var IteratorVariableDeclarationStatement = new CodeVariableDeclarationStatement(typeof(XPathNodeIterator).Name,
                "Iterator", InvokeSelect);
            var IteratorVariableReference = new CodeVariableReferenceExpression(IteratorVariableDeclarationStatement.Name);

            // Results variable declaration statement.
            var GenericListTypeReference = new CodeTypeReference("List", GenericTypeReference);
            var ResultsVariableDeclarationStatement = new CodeVariableDeclarationStatement(
                GenericListTypeReference, "Results", new CodeObjectCreateExpression(GenericListTypeReference));
            var ResultsVariableReference = new CodeVariableReferenceExpression(ResultsVariableDeclarationStatement.Name);

            // While loop test expression.
            var InvokeMoveNext = new CodeMethodInvokeExpression(IteratorVariableReference, "MoveNext");

            // While loop statement.
            var IteratorCurrentPropertyReference = new CodePropertyReferenceExpression(IteratorVariableReference, "Current");
            var InvokeFind = new CodeMethodInvokeExpression(null, "Find", IteratorCurrentPropertyReference);
            var InvokeAddStatement = new CodeMethodInvokeExpression(ResultsVariableReference,
                "Add", InvokeFind);

            var WhileLoopStatement = new CodeIterationStatement(
                new CodeSnippetStatement(), InvokeMoveNext, new CodeSnippetStatement());
            WhileLoopStatement.Statements.Add(InvokeAddStatement);

            // Return results statement.
            var ReturnResultsStatement = new CodeMethodReturnStatement(ResultsVariableReference);

            FindAllMethod.Statements.Add(ResultsVariableDeclarationStatement);
            FindAllMethod.Statements.Add(XPathVariableDeclarationStatement);
            FindAllMethod.Statements.Add(IteratorVariableDeclarationStatement);
            FindAllMethod.Statements.Add(WhileLoopStatement);
            FindAllMethod.Statements.Add(ReturnResultsStatement);

            return FindAllMethod;
        }

        private static CodeMemberMethod CreateFindAllPredicateMethod()
        {
            // Generates the following code:

            // public List<T> FindAll(Predicate<T> match)
            // {
            //     List<T> Results = new List<T>();
            //
            //     foreach (T Item in this)
            //     {
            //         if (match(Item))
            //         {
            //             Results.Add(Item);
            //         }
            //     }
            //
            //     return Results;
            // }

            // Create method.
            var FindAllMethod = new CodeMemberMethod();
            FindAllMethod.Name = "FindAll";
            FindAllMethod.ReturnType = new CodeTypeReference("List", GenericTypeReference);
            FindAllMethod.Parameters.Add(MatchParameterDeclaration);
            FindAllMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            // Results variable declaration statement.
            var GenericListTypeReference = new CodeTypeReference("List", GenericTypeReference);
            var ResultsVariableDeclarationStatement = new CodeVariableDeclarationStatement(
                GenericListTypeReference, "Results", new CodeObjectCreateExpression(GenericListTypeReference));
            var ResultsVariableReference = new CodeVariableReferenceExpression(ResultsVariableDeclarationStatement.Name);

            // Items variable declaration statement.
            var InvokeGetEnumerator = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "GetEnumerator"));
            var ItemsVariableDeclarationStatement = new CodeVariableDeclarationStatement(GenericIEnumeratorTypeReference,
                "Items", InvokeGetEnumerator);
            var ItemsVariableReference = new CodeVariableReferenceExpression(ItemsVariableDeclarationStatement.Name);

            // While loop test expression.
            var InvokeMoveNext = new CodeMethodInvokeExpression(ItemsVariableReference, "MoveNext");

            // While loop if statement.
            var ItemsCurrentPropertyReference = new CodePropertyReferenceExpression(ItemsVariableReference, "Current");
            var InvokeMatch = new CodeDelegateInvokeExpression(MatchArgumentReference, ItemsCurrentPropertyReference);
            var IsMatchCondition = new CodeBinaryOperatorExpression(InvokeMatch,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));
            var InvokeAddStatement = new CodeMethodInvokeExpression(ResultsVariableReference,
                "Add", ItemsCurrentPropertyReference);
            var IfItemIsMatchStatement = new CodeConditionStatement(IsMatchCondition);
            IfItemIsMatchStatement.TrueStatements.Add(InvokeAddStatement);

            var WhileLoopStatement = new CodeIterationStatement(new CodeSnippetStatement(), InvokeMoveNext, new CodeSnippetStatement(), IfItemIsMatchStatement);

            // Return results statement.
            var ReturnResultsStatement = new CodeMethodReturnStatement(ResultsVariableReference);

            FindAllMethod.Statements.Add(ResultsVariableDeclarationStatement);
            FindAllMethod.Statements.Add(ItemsVariableDeclarationStatement);
            FindAllMethod.Statements.Add(WhileLoopStatement);
            FindAllMethod.Statements.Add(ReturnResultsStatement);

            return FindAllMethod;
        }

        #endregion // Custom Members

        #endregion // Collections.

        private static CodeTypeDeclaration CreateContainerBaseClass()
        {
            // Generates the following code:

            // public abstract class ContainerBase
            // {
            //     protected IDataSourceContext _FormContext;
            //     protected string _XPath;
            //     protected bool _IsNew;
            //
            //     public ContainerBase(IDataSourceContext formContext, string xPath)
            //     {
            //         if(formContext == null || string.IsNullOrEmpty(xPath)
            //         {
            //              _IsNew = true;
            //         }
            //         _Context = context;
            //         _XPath = xPath;
            //     }
            //
            //     public virtual void SetValues(IDataSourceContext formContext, string xPath)
            //     {
            //         _FormContext = formContext;
            //         _XPath = xPath;
            //         _IsNew = false;
            //
            //         Type Type = this.GetType();
            //         PropertyInfo[] Properties = Type.GetProperties();
            //         for (int i = 0; (i < Properties.Length); i = (i + 1))
            //         {
            //             PropertyInfo Property = Properties[i];
            //             FieldInfo Field = Type.GetField(("_" + Property.Name), (BindingFlags.Instance | BindingFlags.NonPublic));
            //             if (Property.CanWrite)
            //             {
            //                 Properties[i].SetValue(this, Field.GetValue(this), null);
            //             }
            //         }
            //     }
            //
            //     protected string RemoveIndexesFromXPath(string xPath)
            //     {
            //         return Regex.Replace(xPath, @"\[[0-9]+\]", "");
            //     }
            // }

            var ContainerBaseClass = new CodeTypeDeclaration("ContainerBase");
            ContainerBaseClass.IsClass = true;
            ContainerBaseClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Abstract;

            // Create _Form field.
            var FormContextField = new CodeMemberField(IDataSourceContextTypeReference, "_Context");
            FormContextField.Attributes = MemberAttributes.Family;

            // Create _XPath field.
            var XPathField = new CodeMemberField(StringTypeReference, "_XPath");
            XPathField.Attributes = MemberAttributes.Family;

            // Create _IsNew field.
            var IsNewField = new CodeMemberField(BooleanTypeReference, "_IsNew");
            IsNewField.Attributes = MemberAttributes.Family;
            
            // Create a constructor to set the fields.
            var Constructor = new CodeConstructor();
            Constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            Constructor.Parameters.Add(FormParameterDeclaration);
            Constructor.Parameters.Add(XPathParameterDeclaration);
            Constructor.Statements.Add(new CodeAssignStatement(ContextFieldReference, FormArgumentReference));
            Constructor.Statements.Add(new CodeAssignStatement(XPathFieldReference, XPathArgumentReference));
            var FormArgumentEqualsNull = new CodeBinaryOperatorExpression(FormArgumentReference, 
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(null));
            var XPathArgumentIsNullOrEmpty = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                "IsNullOrEmpty", XPathArgumentReference);
            var Condition = new CodeBinaryOperatorExpression(FormArgumentEqualsNull,
                CodeBinaryOperatorType.BooleanOr, XPathArgumentIsNullOrEmpty);
            var IfStatement = new CodeConditionStatement(Condition);
            IfStatement.TrueStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(null, IsNewField.Name), 
                new CodePrimitiveExpression(true)));
            Constructor.Statements.Add(IfStatement);

            // Add the fields and constructor to the class.
            ContainerBaseClass.Members.Add(FormContextField);
            ContainerBaseClass.Members.Add(XPathField);
            ContainerBaseClass.Members.Add(IsNewField);
            ContainerBaseClass.Members.Add(Constructor);
            ContainerBaseClass.Members.Add(CreateSetValuesMethod(false));
            ContainerBaseClass.Members.Add(CreateRemoveIndexesMethod());
            ContainerBaseClass.Members.Add(CreateRemoveLastIndexMethod());
            ContainerBaseClass.Members.Add(CreateGetNavigatorMethod(false));
            ContainerBaseClass.Members.Add(CreateExistsMethod());

            return ContainerBaseClass;
        }

        private static CodeTypeDeclaration CreateCollectionContainerBaseClass()
        {
            // Generates the following code:

            // public abstract class RepeatingContainerBase : ContainerBase
            // {
            //     protected int _Index;
            //
            //     public RepeatingContainerBase(IDataSourceContext formContext, string xPath, int index)
            //         : base(formContext, xPath)
            //     {
            //         _Index = index;
            //     }
            //
            //     public int Index
            //     {
            //         get { return _Index; }
            //     }
            //
            //     public void SetValues(IDataSourceContext formContext, string xPath, int index)
            //     {
            //         _FormContext = formContext;
            //         _XPath = xPath;
            //         _Index = index;
            //         _IsNew = false;
            //
            //         Type Type = this.GetType();
            //         PropertyInfo[] Properties = Type.GetProperties();
            //         for (int i = 0; (i < Properties.Length); i = (i + 1))
            //         {
            //             PropertyInfo Property = Properties[i];
            //             FieldInfo Field = Type.GetField("_" + Property.Name, BindingFlags.Instance | BindingFlags.NonPublic);
            //             object FieldValue = Field.GetValue(this);
            //
            //             if (Field.FieldType.BaseType == typeof(ContainerBase))
            //             {
            //                 string FieldName = "_" + Property.Name + "XPath";
            //                 FieldInfo XPathField = Type.GetField(FieldName, ((BindingFlags.Instance | BindingFlags.NonPublic) |BindingFlags.Static));
            //                 string GroupXPath = XPathField.GetValue(this).ToString();
            //                 ((ContainerBase)FieldValue).SetValues(_Form, string.Format("{0}[{1}]{2}", _XPath, _Index, GroupXPath));
            //             }
            //             if (Property.CanWrite)
            //             {
            //                 Properties[i].SetValue(this, FieldValue, null);
            //             }
            //         }
            //     }
            // }

            // Create class.
            var CollectionContainerBaseClass = new CodeTypeDeclaration("RepeatingContainerBase");
            CollectionContainerBaseClass.IsClass = true;
            CollectionContainerBaseClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Abstract;
            CollectionContainerBaseClass.BaseTypes.Add(new CodeTypeReference("ContainerBase"));

            // Create _Index field.
            var IndexField = new CodeMemberField(IntegerTypeReference, "_Index");
            IndexField.Attributes = MemberAttributes.Family;
            var IndexFieldReference = new CodeFieldReferenceExpression(null, IndexField.Name);

            // Create a constructor to set the fields in the base class and _Index.
            var Constructor = new CodeConstructor();
            Constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final;

            Constructor.Parameters.Add(FormParameterDeclaration);
            Constructor.Parameters.Add(XPathParameterDeclaration);
            Constructor.Parameters.Add(IndexParameterDeclaration);
            Constructor.BaseConstructorArgs.Add(FormArgumentReference);
            Constructor.BaseConstructorArgs.Add(XPathArgumentReference);
            var IndexAssignStatement = new CodeAssignStatement(IndexFieldReference, IndexArgumentReference);
            Constructor.Statements.Add(IndexAssignStatement);

            // Create Index property.
            var IndexProperty = new CodeMemberProperty();
            IndexProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            IndexProperty.Name = "Index";
            IndexProperty.Type = IntegerTypeReference;
            IndexProperty.Parameters.Add(IndexParameterDeclaration);
            IndexProperty.HasGet = true;
            IndexProperty.GetStatements.Add(new CodeMethodReturnStatement(IndexFieldReference));

            // Add members.
            CollectionContainerBaseClass.Members.Add(IndexField);
            CollectionContainerBaseClass.Members.Add(Constructor);
            CollectionContainerBaseClass.Members.Add(IndexProperty);
            CollectionContainerBaseClass.Members.Add(CreateSetValuesMethod(true));
            CollectionContainerBaseClass.Members.Add(CreateGetNavigatorMethod(true));

            return CollectionContainerBaseClass;
        }

        private static CodeMemberMethod CreateSetValuesMethod(bool isRepeating)
        {
            // Generates the following code (* is only for repeating):

            // public virtual void SetValues(XmlFormHostItem form, string xPath)
            //*public void SetValues(XmlFormHostItem form, string xPath, int index)
            // {
            //     _Form = form;
            //     _XPath = xPath;
            //     _IsNew = false;
            //*    _Index = index;
            //
            //     Type Type = this.GetType();
            //     PropertyInfo[] Properties = Type.GetProperties();
            //     for (int i = 0; (i < Properties.Length); i = (i+1))
            //     {
            //         PropertyInfo Property = Properties[i];
            //         FieldInfo Field = Type.GetField(("_" + Property.Name), (BindingFlags.Instance | BindingFlags.NonPublic));
            //*        object FieldValue = Field.GetValue(this);
            //
            //*        if (Field.FieldType.BaseType == typeof(ContainerBase) && FieldValues != null)
            //*        {
            //*            string FieldName = "_" + Property.Name + "XPath";
            //*            FieldInfo XPathField = Type.GetField(FieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
            //*            string GroupXPath = XPathField.GetValue(this).ToString();
            //*            ((ContainerBase)FieldValue).SetValues(_Form, string.Format("{0}[{1}]{2}", _XPath, _Index, GroupXPath));
            //*        }
            //         if (Property.CanWrite)
            //         {
            //             Properties[i].SetValue(this, Field.GetValue(this), null);
            //         }
            //     }
            // }

            // Create method.
            var SetValuesMethod = new CodeMemberMethod();
            SetValuesMethod.Name = "SetValues";
            SetValuesMethod.Parameters.Add(FormParameterDeclaration);
            SetValuesMethod.Parameters.Add(XPathParameterDeclaration);
            if (isRepeating)
            {
                SetValuesMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                SetValuesMethod.Parameters.Add(IndexParameterDeclaration);
            }
            else
            {
                SetValuesMethod.Attributes = MemberAttributes.Public;
            }

            // Field assignment statements.
            var FormAssignStatement = new CodeAssignStatement(ContextFieldReference, FormArgumentReference);
            var XPathAssignStatement = new CodeAssignStatement(XPathFieldReference, XPathArgumentReference);
            var IsNewAssignStatement = new CodeAssignStatement(new CodeFieldReferenceExpression(null, "_IsNew"),
                new CodePrimitiveExpression(false));
            var IndexAssignStatement = new CodeAssignStatement(IndexFieldReference, IndexArgumentReference);

            // Type variable declaration statement.
            var InvokeGetType = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "GetType");
            var TypeVariableDeclarationStatment = new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(Type).Name),
                "Type", InvokeGetType);
            var TypeVariableReference = new CodeVariableReferenceExpression(TypeVariableDeclarationStatment.Name);

            // Properties variable declaration statement.
            var InvokeGetProperties = new CodeMethodInvokeExpression(TypeVariableReference, "GetProperties");
            var PropertiesVariableDeclarationStatement = new CodeVariableDeclarationStatement(
                new CodeTypeReference(typeof(PropertyInfo[]).Name), "Properties", InvokeGetProperties);
            var PropertiesVariableReference = new CodeVariableReferenceExpression(PropertiesVariableDeclarationStatement.Name);

            // For loop init, test, and increment.
            var PropertiesLengthPropertyReference = new CodePropertyReferenceExpression(PropertiesVariableReference, "Length");
            var ForLoopInitStatement = new CodeVariableDeclarationStatement(IntegerTypeReference, "i", new CodePrimitiveExpression(0));
            var IVariableReference = new CodeVariableReferenceExpression(ForLoopInitStatement.Name);
            var ForLoopTestExpression = new CodeBinaryOperatorExpression(IVariableReference,
                CodeBinaryOperatorType.LessThan, PropertiesLengthPropertyReference);
            var ForLoopIncrementStatement = new CodeAssignStatement(IVariableReference, new CodeBinaryOperatorExpression(
                IVariableReference, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(1)));

            // Property variable declaration statement.
            var PropertyVariableDeclarationStatement = new CodeVariableDeclarationStatement(
                new CodeTypeReference(typeof(PropertyInfo).Name), "Property",
                new CodeIndexerExpression(PropertiesVariableReference, IVariableReference));
            var PropertyVariableReference = new CodeVariableReferenceExpression(PropertyVariableDeclarationStatement.Name);

            // Field variable declaration statement.
            var PropertyVariableNamePropertyReference = new CodePropertyReferenceExpression(PropertyVariableReference, "Name");
            var NamePrependUnderscore = new CodeBinaryOperatorExpression(new CodePrimitiveExpression("_"),
                CodeBinaryOperatorType.Add, PropertyVariableNamePropertyReference);
            var BindingFlagsTypeReference = new CodeTypeReferenceExpression(typeof(BindingFlags).Name);
            var BindingFlags = new CodeBinaryOperatorExpression(
                new CodePropertyReferenceExpression(BindingFlagsTypeReference, "Instance"),
                CodeBinaryOperatorType.BitwiseOr,
                new CodePropertyReferenceExpression(BindingFlagsTypeReference, "NonPublic"));
            var InvokeGetField = new CodeMethodInvokeExpression(TypeVariableReference, "GetField", NamePrependUnderscore, BindingFlags);
            var FieldInfoTypeReference = new CodeTypeReference(typeof(FieldInfo).Name);
            var FieldVariableDeclarationStatement = new CodeVariableDeclarationStatement(FieldInfoTypeReference, "Field", InvokeGetField);
            var FieldVariableReference = new CodeVariableReferenceExpression(FieldVariableDeclarationStatement.Name);

            // FieldValue variable declaration statement.
            var InvokeFieldGetValue = new CodeMethodInvokeExpression(FieldVariableReference, "GetValue",
                new CodeThisReferenceExpression());
            var FieldValueVariableDeclarationStatement = new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(object)),
                "FieldValue", InvokeFieldGetValue);
            var FieldValueVariableReference = new CodeVariableReferenceExpression(FieldValueVariableDeclarationStatement.Name);

            // If field base type is ContainerBase statement.
            var InvokeStringFormat = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference), "Format",
                new CodePrimitiveExpression("_{0}XPath"), PropertyVariableNamePropertyReference);
            var FieldNameVariableDeclarationStatement = new CodeVariableDeclarationStatement(StringTypeReference, "FieldName",
                InvokeStringFormat);
            var FieldNameVariableReference = new CodeVariableReferenceExpression(FieldNameVariableDeclarationStatement.Name);

            var BindingFlagsWithStatic = new CodeBinaryOperatorExpression(BindingFlags, CodeBinaryOperatorType.BitwiseOr,
                new CodePropertyReferenceExpression(BindingFlagsTypeReference, "Static"));
            var InvokeTypeGetField = new CodeMethodInvokeExpression(TypeVariableReference, "GetField", FieldNameVariableReference,
                BindingFlagsWithStatic);
            var XPathFieldVariableDeclarationStatement = new CodeVariableDeclarationStatement(FieldInfoTypeReference,
                "XPathField", InvokeTypeGetField);
            var XPathFieldVariableReference = new CodeVariableReferenceExpression(XPathFieldVariableDeclarationStatement.Name);

            var InvokeXPathFieldGetValue = new CodeMethodInvokeExpression(XPathFieldVariableReference, "GetValue",
                new CodeThisReferenceExpression());
            var InvokeGetValueToString = new CodeMethodInvokeExpression(InvokeXPathFieldGetValue, "ToString");
            var GroupXPathVariableDeclarationStatment = new CodeVariableDeclarationStatement(StringTypeReference, "GroupXPath",
                InvokeGetValueToString);
            var GroupXPathVariableReference = new CodeVariableReferenceExpression(GroupXPathVariableDeclarationStatment.Name);

            var CastFieldValueToContainerBase = new CodeCastExpression(new CodeTypeReference("ContainerBase"), FieldValueVariableReference);
            var InvokeGroupXPathStringFormat = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                "Format", new CodePrimitiveExpression("{0}[{1}]{2}"), XPathFieldReference, IndexFieldReference, GroupXPathVariableReference);
            var InvokeFieldValueSetValues = new CodeMethodInvokeExpression(CastFieldValueToContainerBase, "SetValues",
                ContextFieldReference, InvokeGroupXPathStringFormat);

            var FieldTypeProperty = new CodePropertyReferenceExpression(FieldVariableReference, "FieldType");
            var BaseTypeProperty = new CodePropertyReferenceExpression(FieldTypeProperty, "BaseType");
            var FieldBaseTypeEqualsContainerBaseTypeConditionAndNotNull = new CodeBinaryOperatorExpression(
                new CodeBinaryOperatorExpression(BaseTypeProperty,
                CodeBinaryOperatorType.IdentityEquality, new CodeTypeOfExpression("ContainerBase")), CodeBinaryOperatorType.BooleanAnd,
                new CodeBinaryOperatorExpression(FieldValueVariableReference, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)));

            var IfBaseTypeEqualsContainerBaseTypeStatement = new CodeConditionStatement(FieldBaseTypeEqualsContainerBaseTypeConditionAndNotNull);
            IfBaseTypeEqualsContainerBaseTypeStatement.TrueStatements.Add(FieldNameVariableDeclarationStatement);
            IfBaseTypeEqualsContainerBaseTypeStatement.TrueStatements.Add(XPathFieldVariableDeclarationStatement);
            IfBaseTypeEqualsContainerBaseTypeStatement.TrueStatements.Add(GroupXPathVariableDeclarationStatment);
            IfBaseTypeEqualsContainerBaseTypeStatement.TrueStatements.Add(InvokeFieldValueSetValues);

            // If Property.CanWrite statement.
            var PropertyCanWritePropertyReference = new CodePropertyReferenceExpression(PropertyVariableReference, "CanWrite");
            var CanWriteTrueCondition = new CodeBinaryOperatorExpression(PropertyCanWritePropertyReference,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true));

            // Invoke set value statement.
            var PropertiesIndexer = new CodeIndexerExpression(PropertiesVariableReference, IVariableReference);
            var InvokePropertySetValue = new CodeMethodInvokeExpression(PropertiesIndexer, "SetValue", new CodeThisReferenceExpression(),
                FieldValueVariableReference, new CodePrimitiveExpression(null));
            var IfPropertyCanWriteStatement = new CodeConditionStatement(CanWriteTrueCondition);
            IfPropertyCanWriteStatement.TrueStatements.Add(InvokePropertySetValue);

            // For loop statement.
            var ForLoopStatement = new CodeIterationStatement(ForLoopInitStatement, ForLoopTestExpression, ForLoopIncrementStatement);
            ForLoopStatement.Statements.Add(PropertyVariableDeclarationStatement);
            ForLoopStatement.Statements.Add(FieldVariableDeclarationStatement);
            ForLoopStatement.Statements.Add(FieldValueVariableDeclarationStatement);
            if (isRepeating)
            {
                ForLoopStatement.Statements.Add(IfBaseTypeEqualsContainerBaseTypeStatement);
            }
            ForLoopStatement.Statements.Add(IfPropertyCanWriteStatement);

            // Add statements.
            SetValuesMethod.Statements.Add(FormAssignStatement);
            SetValuesMethod.Statements.Add(XPathAssignStatement);
            SetValuesMethod.Statements.Add(IsNewAssignStatement);
            if (isRepeating)
            {
                SetValuesMethod.Statements.Add(IndexAssignStatement);
            }
            SetValuesMethod.Statements.Add(TypeVariableDeclarationStatment);
            SetValuesMethod.Statements.Add(PropertiesVariableDeclarationStatement);
            SetValuesMethod.Statements.Add(ForLoopStatement);

            return SetValuesMethod;
        }

        private static CodeMemberMethod CreateRemoveIndexesMethod()
        {
            // Create Method.
            var RemoveIndexesMethod = new CodeMemberMethod();
            RemoveIndexesMethod.Name = "RemoveIndexesFromXPath";
            RemoveIndexesMethod.Attributes = MemberAttributes.Family | MemberAttributes.Final;
            RemoveIndexesMethod.ReturnType = StringTypeReference;
            RemoveIndexesMethod.Parameters.Add(XPathParameterDeclaration);

            // Type References.
            var RegexTypeReference = new CodeTypeReference(typeof(System.Text.RegularExpressions.Regex).Name);

            // Regex pattern.
            var RegexPattern = new CodePrimitiveExpression(@"\[[0-9]+\]");

            // Regex.Replace method reference.
            var ReplaceMethodReference = new CodeMethodReferenceExpression(
                new CodeTypeReferenceExpression(RegexTypeReference), "Replace");

            // Invoke Regex.Replace
            var InvokeReplace = new CodeMethodInvokeExpression(ReplaceMethodReference, XPathArgumentReference, RegexPattern,
                new CodePrimitiveExpression(string.Empty));

            // Return statement.
            var ReturnStatement = new CodeMethodReturnStatement(InvokeReplace);

            RemoveIndexesMethod.Statements.Add(ReturnStatement);

            return RemoveIndexesMethod;
        }

        private static CodeMemberMethod CreateRemoveLastIndexMethod()
        {
            // Generates the following code:

            // protected string RemoveLastIndexFromXPath(string xPath)
            // {
            //    Regex MatchIndexes = new Regex("\\[[0-9]+\\]", RegexOptions.RightToLeft);
            //     return MatchIndexes.Replace(xPath, "", 1);
            // }

            // Create Method.
            var RemoveLastIndexMethod = new CodeMemberMethod();
            RemoveLastIndexMethod.Name = "RemoveLastIndexFromXPath";
            RemoveLastIndexMethod.Attributes = MemberAttributes.Family | MemberAttributes.Final;
            RemoveLastIndexMethod.ReturnType = StringTypeReference;
            RemoveLastIndexMethod.Parameters.Add(XPathParameterDeclaration);

            // Type References.
            var RegexTypeReference = new CodeTypeReference(typeof(System.Text.RegularExpressions.Regex).Name);
            var RegexOptionsTypeReference = new CodeTypeReference(typeof(System.Text.RegularExpressions.RegexOptions).Name);

            // Regex pattern.
            var RegexPattern = new CodePrimitiveExpression(@"\[[0-9]+\]");

            // Regex options.
            var RightToLeftOption = new CodePropertyReferenceExpression(
                new CodeTypeReferenceExpression(RegexOptionsTypeReference), "RightToLeft");

            // MatchIndexes variable declaration statement.
            var MatchIndexesVariableDeclarationStatement = new CodeVariableDeclarationStatement(RegexTypeReference, 
                "MatchIndexes", new CodeObjectCreateExpression(RegexTypeReference, RegexPattern, RightToLeftOption));
            var MatchIndexesVariableReference = new CodeVariableReferenceExpression(MatchIndexesVariableDeclarationStatement.Name);

            // Regex.Replace method reference.
            var InvokeReplaceMethod = new CodeMethodInvokeExpression(MatchIndexesVariableReference, "Replace",
                XPathArgumentReference, new CodePrimitiveExpression(string.Empty), new CodePrimitiveExpression(1));


            // Return statement.
            var ReturnStatement = new CodeMethodReturnStatement(InvokeReplaceMethod);

            RemoveLastIndexMethod.Statements.Add(MatchIndexesVariableDeclarationStatement);
            RemoveLastIndexMethod.Statements.Add(ReturnStatement);

            return RemoveLastIndexMethod;
        }

        private static CodeMemberMethod CreateGetNavigatorMethod(bool isRepeating)
        {
            // Generates the following code:

            // protected XPathNavigator GetNavigator()
            // {
            //     return NodeOperations.GetNavigator(_FormContext, _XPath);
            // }

            // Create Method.
            var GetNavigatorMethod = new CodeMemberMethod();
            GetNavigatorMethod.Name = "GetNavigator";
            GetNavigatorMethod.Attributes = MemberAttributes.Public;
            if (isRepeating)
            {
                GetNavigatorMethod.Attributes |= MemberAttributes.Override;
            }
            GetNavigatorMethod.ReturnType = XPathNavigatorTypeReference;

            // Return statement.
            CodeExpression XPathExpression;
            if (isRepeating)
            {
                XPathExpression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(StringTypeReference),
                    "Format", new CodePrimitiveExpression("{0}[{1}]"), XPathFieldReference, IndexFieldReference);
            }
            else
            {
                XPathExpression = XPathFieldReference;
            }
            var InvokeGetNavigator = new CodeMethodInvokeExpression(NodeOperationsReference, 
                "GetNavigator", ContextFieldReference, XPathExpression);
            var ReturnStatement = new CodeMethodReturnStatement(InvokeGetNavigator);

            GetNavigatorMethod.Statements.Add(ReturnStatement);

            return GetNavigatorMethod;
        }

        private static CodeMemberMethod CreateExistsMethod()
        {
            // Generates the following code:

            // public bool Exists()
            // {
            //     return GetNavigator() != null;
            // }

            // Create Method.
            var ExistsMethod = new CodeMemberMethod();
            ExistsMethod.Name = "Exists";
            ExistsMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            ExistsMethod.ReturnType = BooleanTypeReference;

            var InvokeGetNavigator = new CodeMethodInvokeExpression(null, "GetNavigator");
            var GetNavigatorNotEqualNull = new CodeBinaryOperatorExpression(InvokeGetNavigator,
                CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
            var ReturnStatement = new CodeMethodReturnStatement(GetNavigatorNotEqualNull);

            ExistsMethod.Statements.Add(ReturnStatement);

            return ExistsMethod;
        }

        private static CodeMemberMethod CreateGetNodeMethod(bool isRoot)
        {
            // Generates the following code (* is only for root):

            // public XPathNavigator GetNode(string xPath)
            // {
            //*    XPathNavigator Node = NodeOperations.GetNavigator(this, xPath);
            //     XPathNavigator Node = NodeOperations.GetNavigator(_Form, xPath);
            //     if (Node == null)
            //     {
            //         throw new PropertyAccessException(xPath);
            //     }
            //     return Node;
            // }

            var GetNodeMethod = new CodeMemberMethod();

            return GetNodeMethod;
        }

        private static CodeMemberField CreateLastAccessFailedField()
        {
            // Generates the following code;

            // private static bool _LastAccessFailed;

            var LastAccessFailedField = new CodeMemberField(BooleanTypeReference, "_LastAccessFailed");
            LastAccessFailedField.Attributes = MemberAttributes.Private | MemberAttributes.Static;

            return LastAccessFailedField;
        }

        private static CodeMemberProperty CreateLastAccessFailedProperty()
        {
            // Generates the following code;

            // public static bool LastAccessFailed
            // {
            //     get { return _LastAccessFailed; }
            //     set { _LastAccessFailed = value; }
            // }

            var LastAccessFailedProperty = new CodeMemberProperty();
            LastAccessFailedProperty.Name = "LastAccessFailed";
            LastAccessFailedProperty.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            LastAccessFailedProperty.Type = BooleanTypeReference;
            LastAccessFailedProperty.HasGet = true;
            LastAccessFailedProperty.HasSet = true;

            var LastAccessFailedFieldReference = new CodeFieldReferenceExpression(null, "_" + LastAccessFailedProperty.Name);

            // Get method.
            LastAccessFailedProperty.GetStatements.Add(new CodeMethodReturnStatement(LastAccessFailedFieldReference));

            // Set sethod.
            LastAccessFailedProperty.SetStatements.Add(new CodeAssignStatement(LastAccessFailedFieldReference,
                new CodePropertySetValueReferenceExpression()));

            return LastAccessFailedProperty;
        }

        private static CodeMemberField CreateContextField()
        {
            // Generates the following code;

            // private IDataSourceContext _Context;

            var FormContextField = new CodeMemberField(IDataSourceContextTypeReference, "_Context");
            FormContextField.Attributes = MemberAttributes.Private;

            return FormContextField;
        }

        private static CodeMemberProperty CreateContextProperty()
        {
            // Generates the following code;

            // public IDataSourceContext Context
            // {
            //     get 
            //     { 
            //         if(_Context == null)
            //         {
            //             _Context = new FormContext(this);
            //         }
            //         return _Context;  
            //     }
            // }

            var ContextProperty = new CodeMemberProperty();
            ContextProperty.Name = "Context";
            ContextProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            ContextProperty.Type = IDataSourceContextTypeReference;
            ContextProperty.HasGet = true;

            var FormContextFieldReference = new CodeFieldReferenceExpression(null, "_" + ContextProperty.Name);

            // Get method.
            var FormContextEqualsNullCondtion = new CodeBinaryOperatorExpression(FormContextFieldReference,
                CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(null));
            var ReturnFormContextStatement = new CodeMethodReturnStatement(FormContextFieldReference);
            var FormContextAssignStatment = new CodeAssignStatement(FormContextFieldReference,
                new CodeObjectCreateExpression(new CodeTypeReference(typeof(FormContext).Name),
                new CodeThisReferenceExpression()));
            var IfFormContextEqualsNullStatement = new CodeConditionStatement(FormContextEqualsNullCondtion);
            IfFormContextEqualsNullStatement.TrueStatements.Add(FormContextAssignStatment);

            ContextProperty.GetStatements.Add(IfFormContextEqualsNullStatement);
            ContextProperty.GetStatements.Add(ReturnFormContextStatement);

            return ContextProperty;
        }

        [Flags]
        private enum ChildTypeOptions
        {
            None                    = 0,
            IsAttribute             = 1 << 0,
            ParentIsGroup           = 1 << 1,
            ParentIsRepeating       = 1 << 2,
            HasRepeatingAncestor    = 1 << 3
        }

        /// <summary>
        /// Prvides options for how properties should be generated.
        /// Default is to include all fields/groups in a hierarchy matching the form MainDataSource
        /// </summary>
        [Flags]
        public enum PropertyGenerationOptions
        {
            /// <summary>
            /// If no options are selected the defaults will be used.
            /// </summary>
            None                    = 0,
            /// <summary>
            /// Properties are not created for attibutes.
            /// </summary>
            ExcludeAttributes       = 1 << 0,
            /// <summary>
            /// Properties are not created for fields.
            /// </summary>
            ExcludeFields           = 1 << 1,
            /// <summary>
            /// Properties are not created for repeating fields.
            /// </summary>
            ExcludeRepeatingFields  = 1 << 2,
            /// <summary>
            /// Properties are not created for groups.
            /// </summary>
            ExcludeGroups           = 1 << 3,
            /// <summary>
            /// Properties are not created for repeating groups.
            /// </summary>
            ExcludeRepeatingGroups  = 1 << 4,
            /// <summary>
            /// Properties are flattened for access e.g. Fields.FieldName or Attibutes.AttibuteName.
            /// </summary>
            // TODO: Implement this option.
            //GroupByType             = 1 << 6,
            /// <summary>
            /// Properties are not generated for nested elements.
            /// </summary>
            RootOnly                = 1 << 5,
            /// <summary>
            /// Properties will not be created for any repeating elements.
            /// </summary>
            ExcludeAllRepeatingElements = ExcludeRepeatingFields | ExcludeRepeatingGroups,
            /// <summary>
            /// Properties will only be generated for fields in the root of the data source.
            /// </summary>
            IncludeOnlyFields = ExcludeAllRepeatingElements | ExcludeGroups | ExcludeAttributes
        }
    }

    public class PropertyAccessException : Exception
    {
        const string MESSAGE = "Property could not be accessed, most likely the DataSource has been changed and the properties have not been generated";

        public PropertyAccessException() : base (MESSAGE) { }

        public PropertyAccessException(string xPath) : base(MESSAGE)
        {
            XPath = xPath;
        }

        public string XPath { get; private set; }

        public override string Message
        {
            get
            {
                return base.Message + "\r\n" +
                    "XPath: \"" + XPath + "\"";
            }
        }
    }
}